summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBryan Paxton <39971740+starbelly@users.noreply.github.com>2019-03-07 10:54:01 -0600
committerTristan Sloughter <t@crashfast.com>2019-03-07 09:54:01 -0700
commit209c02ec57c2cc3207ee0174c3af3675b8dc8f79 (patch)
treee07b25a27a2c4cc438db75d8d066179b4e57f3f7 /src
parent9e2e54afd74104ecb05c55e033803f41932eb940 (diff)
Fix fetching of private packages from orgs on hex repos (#2020)
- vendor in hex_core at v0.5.0 - Change where repo_name should be the org and not the parent - Changed rebar_utils:url_append_path/2 to not explicitly add a '?', this is returned in the Query chunk by http_uri:parse/1 (e.g., "?foo=bar") - update organization_merging test to expect the sub-repo as the repo_name - Add tests for rebar_utils:url_append_path/2 - Stop referencing/setting "organization" in config and use new organization settings (api_repository and repo_organization) - Do not set (assume) the read key is valid for any/every repo - Set repo_organization and api_repository to org - Update tests to check for new config opts
Diffstat (limited to 'src')
-rw-r--r--src/r3_hex_api.erl120
-rw-r--r--src/r3_hex_api_key.erl31
-rw-r--r--src/r3_hex_api_package.erl49
-rw-r--r--src/r3_hex_api_package_owner.erl34
-rw-r--r--src/r3_hex_api_release.erl60
-rw-r--r--src/r3_hex_api_user.erl46
-rw-r--r--src/r3_hex_core.erl92
-rw-r--r--src/r3_hex_core.hrl3
-rw-r--r--src/r3_hex_erl_tar.erl1966
-rw-r--r--src/r3_hex_erl_tar.hrl405
-rw-r--r--src/r3_hex_filename.erl60
-rw-r--r--src/r3_hex_http.erl43
-rw-r--r--src/r3_hex_http_httpc.erl39
-rw-r--r--src/r3_hex_pb_names.erl735
-rw-r--r--src/r3_hex_pb_package.erl1699
-rw-r--r--src/r3_hex_pb_signed.erl564
-rw-r--r--src/r3_hex_pb_versions.erl958
-rw-r--r--src/r3_hex_registry.erl133
-rw-r--r--src/r3_hex_repo.erl174
-rw-r--r--src/r3_hex_tarball.erl507
-rw-r--r--src/r3_safe_erl_term.erl678
-rw-r--r--src/r3_safe_erl_term.xrl79
-rw-r--r--src/rebar.app.src1
-rw-r--r--src/rebar_hex_repos.erl27
-rw-r--r--src/rebar_packages.erl8
-rw-r--r--src/rebar_pkg_resource.erl6
-rw-r--r--src/rebar_utils.erl2
27 files changed, 8498 insertions, 21 deletions
diff --git a/src/r3_hex_api.erl b/src/r3_hex_api.erl
new file mode 100644
index 0000000..09d9bcf
--- /dev/null
+++ b/src/r3_hex_api.erl
@@ -0,0 +1,120 @@
+%% Vendored from hex_core v0.5.0, do not edit manually
+
+%% @hidden
+
+-module(r3_hex_api).
+
+-export([
+ delete/2,
+ get/2,
+ post/3,
+ put/3,
+ encode_query_string/1,
+ build_repository_path/2,
+ build_organization_path/2,
+ join_path_segments/1
+]).
+-define(ERL_CONTENT_TYPE, <<"application/vnd.hex+erlang">>).
+
+get(Config, Path) ->
+ request(Config, get, Path, undefined).
+
+post(Config, Path, Body) ->
+ request(Config, post, Path, encode_body(Body)).
+
+put(Config, Path, Body) ->
+ request(Config, put, Path, encode_body(Body)).
+
+delete(Config, Path) ->
+ request(Config, delete, Path, undefined).
+
+%% @private
+encode_query_string(List) ->
+ QueryString =
+ join("&",
+ lists:map(fun
+ ({K, V}) when is_atom(V) ->
+ atom_to_list(K) ++ "=" ++ atom_to_list(V);
+ ({K, V}) when is_binary(V) ->
+ atom_to_list(K) ++ "=" ++ binary_to_list(V);
+ ({K, V}) when is_integer(V) ->
+ atom_to_list(K) ++ "=" ++ integer_to_list(V)
+ end, List)),
+ Encoded = http_uri:encode(QueryString),
+ list_to_binary(Encoded).
+
+%% @private
+build_repository_path(#{api_repository := Repo}, Path) when is_binary(Repo) ->
+ ["repos", Repo | Path];
+build_repository_path(#{api_repository := undefined}, Path) ->
+ Path.
+
+%% @private
+build_organization_path(#{api_organization := Org}, Path) when is_binary(Org) ->
+ ["orgs", Org | Path];
+build_organization_path(#{api_organization := undefined}, Path) ->
+ Path.
+
+%% @private
+join_path_segments(Segments) ->
+ erlang:iolist_to_binary(join(<<"/">>, lists:map(fun encode/1, Segments))).
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+
+request(Config, Method, PathSegments, Body) when is_list(PathSegments) ->
+ Path = join_path_segments(PathSegments),
+ request(Config, Method, Path, Body);
+request(Config, Method, Path, Body) when is_binary(Path) and is_map(Config) ->
+ DefaultHeaders = make_headers(Config),
+ ReqHeaders = maps:merge(maps:get(http_headers, Config, #{}), DefaultHeaders),
+ ReqHeaders2 = put_new(<<"accept">>, ?ERL_CONTENT_TYPE, ReqHeaders),
+
+ case r3_hex_http:request(Config, Method, build_url(Path, Config), ReqHeaders2, Body) of
+ {ok, {Status, RespHeaders, RespBody}} = Response ->
+ ContentType = maps:get(<<"content-type">>, RespHeaders, <<"">>),
+ case binary:match(ContentType, ?ERL_CONTENT_TYPE) of
+ {_, _} ->
+ {ok, {Status, RespHeaders, binary_to_term(RespBody)}};
+
+ nomatch ->
+ Response
+ end;
+
+ Other ->
+ Other
+ end.
+
+encode(Binary) when is_binary(Binary) ->
+ encode(binary_to_list(Binary));
+encode(String) when is_list(String) ->
+ http_uri:encode(String).
+
+build_url(Path, #{api_url := URI}) ->
+ <<URI/binary, "/", Path/binary>>.
+
+encode_body({_ContentType, _Body} = Body) ->
+ Body;
+encode_body(Body) ->
+ {binary_to_list(?ERL_CONTENT_TYPE), term_to_binary(Body)}.
+
+%% TODO: copy-pasted from r3_hex_repo
+make_headers(Config) ->
+ maps:fold(fun set_header/3, #{}, Config).
+
+set_header(api_key, Token, Headers) when is_binary(Token) -> maps:put(<<"authorization">>, Token, Headers);
+set_header(_, _, Headers) -> Headers.
+
+put_new(Key, Value, Map) ->
+ case maps:find(Key, Map) of
+ {ok, _} -> Map;
+ error -> maps:put(Key, Value, Map)
+ end.
+
+%% https://github.com/erlang/otp/blob/OTP-20.3/lib/stdlib/src/lists.erl#L1449:L1453
+join(_Sep, []) -> [];
+join(Sep, [H|T]) -> [H|join_prepend(Sep, T)].
+
+join_prepend(_Sep, []) -> [];
+join_prepend(Sep, [H|T]) -> [Sep,H|join_prepend(Sep,T)].
diff --git a/src/r3_hex_api_key.erl b/src/r3_hex_api_key.erl
new file mode 100644
index 0000000..68c0b13
--- /dev/null
+++ b/src/r3_hex_api_key.erl
@@ -0,0 +1,31 @@
+%% Vendored from hex_core v0.5.0, do not edit manually
+
+-module(r3_hex_api_key).
+-export([
+ list/1,
+ get/2,
+ add/3,
+ delete/2,
+ delete_all/1
+]).
+
+list(Config) when is_map(Config) ->
+ Path = r3_hex_api:build_organization_path(Config, ["keys"]),
+ r3_hex_api:get(Config, Path).
+
+get(Config, Name) when is_map(Config) ->
+ Path = r3_hex_api:build_organization_path(Config, ["keys", Name]),
+ r3_hex_api:get(Config, Path).
+
+add(Config, Name, Permissions) when is_map(Config) ->
+ Path = r3_hex_api:build_organization_path(Config, ["keys"]),
+ Params = #{<<"name">> => Name, <<"permissions">> => Permissions},
+ r3_hex_api:post(Config, Path, Params).
+
+delete(Config, Name) when is_map(Config) ->
+ Path = r3_hex_api:build_organization_path(Config, ["keys", Name]),
+ r3_hex_api:delete(Config, Path).
+
+delete_all(Config) when is_map(Config) ->
+ Path = r3_hex_api:build_organization_path(Config, ["keys"]),
+ r3_hex_api:delete(Config, Path).
diff --git a/src/r3_hex_api_package.erl b/src/r3_hex_api_package.erl
new file mode 100644
index 0000000..903adc0
--- /dev/null
+++ b/src/r3_hex_api_package.erl
@@ -0,0 +1,49 @@
+%% Vendored from hex_core v0.5.0, do not edit manually
+
+-module(r3_hex_api_package).
+-export([get/2, search/3]).
+
+%% @doc
+%% Gets package.
+%%
+%% Examples:
+%%
+%% ```
+%% > r3_hex_api_package:get(r3_hex_core:default_config(), <<"package">>).
+%% {ok, {200, ..., #{
+%% <<"name">> => <<"package1">>,
+%% <<"meta">> => #{
+%% <<"description">> => ...,
+%% <<"licenses">> => ...,
+%% <<"links">> => ...,
+%% <<"maintainers">> => ...
+%% },
+%% ...,
+%% <<"releases">> => [
+%% #{<<"url">> => ..., <<"version">> => <<"0.5.0">>}],
+%% #{<<"url">> => ..., <<"version">> => <<"1.0.0">>}],
+%% ...
+%% ]}}}
+%% '''
+%% @end
+get(Config, Name) when is_binary(Name) and is_map(Config) ->
+ Path = r3_hex_api:build_repository_path(Config, ["packages", Name]),
+ r3_hex_api:get(Config, Path).
+
+%% @doc
+%% Searches packages.
+%%
+%% Examples:
+%%
+%% ```
+%% > r3_hex_api_package:search(r3_hex_core:default_config(), <<"package">>, []).
+%% {ok, {200, ..., [
+%% #{<<"name">> => <<"package1">>, ...},
+%% ...
+%% ]}}
+%% '''
+search(Config, Query, SearchParams) when is_binary(Query) and is_list(SearchParams) and is_map(Config) ->
+ QueryString = r3_hex_api:encode_query_string([{search, Query} | SearchParams]),
+ Path = r3_hex_api:join_path_segments(r3_hex_api:build_repository_path(Config, ["packages"])),
+ PathQuery = <<Path/binary, "?", QueryString/binary>>,
+ r3_hex_api:get(Config, PathQuery).
diff --git a/src/r3_hex_api_package_owner.erl b/src/r3_hex_api_package_owner.erl
new file mode 100644
index 0000000..3a83b43
--- /dev/null
+++ b/src/r3_hex_api_package_owner.erl
@@ -0,0 +1,34 @@
+%% Vendored from hex_core v0.5.0, do not edit manually
+
+-module(r3_hex_api_package_owner).
+-export([
+ add/3,
+ delete/3,
+ get/3,
+ list/2
+]).
+
+%% Examples:
+%%
+%% ```
+%% > r3_hex_api_owner:list(r3_hex_core:default_config(), <<"package">>).
+%% {ok, {200, ..., [
+%% #{<<"username">> => <<"alice">>, ...},
+%% ...
+%% ]}}
+%% '''
+list(Config, PackageName) when is_binary(PackageName) and is_map(Config) ->
+ Path = r3_hex_api:build_repository_path(Config, ["packages", PackageName, "owners"]),
+ r3_hex_api:get(Config, Path).
+
+get(Config, PackageName, UsernameOrEmail) when is_binary(PackageName) and is_map(Config) ->
+ Path = r3_hex_api:build_repository_path(Config, ["packages", PackageName, "owners", UsernameOrEmail]),
+ r3_hex_api:get(Config, Path).
+
+add(Config, PackageName, UsernameOrEmail) when is_binary(PackageName) and is_map(Config) ->
+ Path = r3_hex_api:build_repository_path(Config, ["packages", PackageName, "owners", UsernameOrEmail]),
+ r3_hex_api:put(Config, Path, #{}).
+
+delete(Config, PackageName, UsernameOrEmail) when is_binary(PackageName) and is_map(Config) ->
+ Path = r3_hex_api:build_repository_path(Config, ["packages", PackageName, "owners", UsernameOrEmail]),
+ r3_hex_api:delete(Config, Path).
diff --git a/src/r3_hex_api_release.erl b/src/r3_hex_api_release.erl
new file mode 100644
index 0000000..4acda0e
--- /dev/null
+++ b/src/r3_hex_api_release.erl
@@ -0,0 +1,60 @@
+%% Vendored from hex_core v0.5.0, do not edit manually
+
+-module(r3_hex_api_release).
+-export([
+ delete/3,
+ get/3,
+ publish/2,
+ retire/4,
+ unretire/3
+]).
+
+%% @doc
+%% Gets package release.
+%%
+%% Examples:
+%%
+%% ```
+%% > r3_hex_api:get_release(<<"package">>, <<"1.0.0">>, r3_hex_core:default_config()).
+%% {ok, {200, ..., #{
+%% <<"version">> => <<"1.0.0">>,
+%% <<"meta">> => #{
+%% <<"description">> => ...,
+%% <<"licenses">> => ...,
+%% <<"links">> => ...,
+%% <<"maintainers">> => ...
+%% },
+%% ...}}}
+%% '''
+%% @end
+get(Config, Name, Version) when is_binary(Name) and is_binary(Version) and is_map(Config) ->
+ Path = r3_hex_api:build_repository_path(Config, ["packages", Name, "releases", Version]),
+ r3_hex_api:get(Config, Path).
+
+publish(Config, Tarball) when is_binary(Tarball) and is_map(Config) ->
+ Path = r3_hex_api:build_repository_path(Config, ["publish"]),
+ TarballContentType = "application/octet-stream",
+ Config2 = put_header(<<"content-length">>, integer_to_binary(byte_size(Tarball)), Config),
+ Body = {TarballContentType, Tarball},
+ r3_hex_api:post(Config2, Path, Body).
+
+delete(Config, Name, Version) when is_binary(Name) and is_binary(Version) and is_map(Config) ->
+ Path = r3_hex_api:build_repository_path(Config, ["packages", Name, "releases", Version]),
+ r3_hex_api:delete(Config, Path).
+
+retire(Config, Name, Version, Params) when is_binary(Name) and is_binary(Version) and is_map(Config) ->
+ Path = r3_hex_api:build_repository_path(Config, ["packages", Name, "releases", Version, "retire"]),
+ r3_hex_api:post(Config, Path, Params).
+
+unretire(Config, Name, Version) when is_binary(Name) and is_binary(Version) and is_map(Config) ->
+ Path = r3_hex_api:build_repository_path(Config, ["packages", Name, "releases", Version, "retire"]),
+ r3_hex_api:delete(Config, Path).
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+
+put_header(Name, Value, Config) ->
+ Headers = maps:get(http_headers, Config, #{}),
+ Headers2 = maps:put(Name, Value, Headers),
+ maps:put(http_headers, Headers2, Config).
diff --git a/src/r3_hex_api_user.erl b/src/r3_hex_api_user.erl
new file mode 100644
index 0000000..67d32ce
--- /dev/null
+++ b/src/r3_hex_api_user.erl
@@ -0,0 +1,46 @@
+%% Vendored from hex_core v0.5.0, do not edit manually
+
+-module(r3_hex_api_user).
+-export([
+ create/4,
+ get/2,
+ me/1,
+ reset_password/2
+]).
+
+me(Config) when is_map(Config) ->
+ r3_hex_api:get(Config, ["users", "me"]).
+
+create(Config, Username, Password, Email) ->
+ Params = #{
+ <<"username">> => Username,
+ <<"password">> => Password,
+ <<"email">> => Email
+ },
+ r3_hex_api:post(Config, ["users"], Params).
+
+reset_password(Username, Config) when is_binary(Username) and is_map(Config) ->
+ r3_hex_api:post(Config, ["users", Username, "reset"], #{}).
+
+%% @doc
+%% Gets user.
+%%
+%% Examples:
+%%
+%% ```
+%% > r3_hex_api_user:get(<<"user">>, r3_hex_core:default_config()).
+%% {ok, {200, ..., #{
+%% <<"username">> => <<"user">>,
+%% <<"packages">> => [
+%% #{
+%% <<"name">> => ...,
+%% <<"url">> => ...,
+%% ...
+%% },
+%% ...
+%% ],
+%% ...}}}
+%% '''
+%% @end
+get(Config, Username) when is_binary(Username) and is_map(Config) ->
+ r3_hex_api:get(Config, ["users", Username]).
diff --git a/src/r3_hex_core.erl b/src/r3_hex_core.erl
new file mode 100644
index 0000000..84eabe6
--- /dev/null
+++ b/src/r3_hex_core.erl
@@ -0,0 +1,92 @@
+%% Vendored from hex_core v0.5.0, do not edit manually
+
+%% @doc
+%% hex_core entrypoint module.
+%%
+%% ### Config
+%%
+%% Most functions in the hex_core API takes a configuration. The configuration sets things
+%% like HTTP client to use, and API and repository URL. Some of these configuration options
+%% will likely be static for your application and some may change depending on the function
+%% you call.
+%%
+%% ##### Options
+%%
+%% * `api_key' - Authentication key used when accessing the HTTP API.
+%% * `api_organization' - Name of the organization endpoint in the API, this should
+%% for example be set when accessing key for a specific organization.
+%% * `api_repository' - Name of the repository endpoint in the API, this should
+%% for example be set when accessing packages from a specific repository.
+%% * `api_url' - URL to the HTTP API (default: `https://hex.pm/api').
+%% * `http_adapter' - Callback module used for HTTP requests, see [`r3_hex_http'](r3_hex_http.html)
+%% (default: `r3_hex_http_httpc').
+%% * `http_etag' - Sets the `if-none-match' HTTP header with the given value to do a
+%% conditional HTTP request.
+%% * `http_adapter_config' - Configuration to pass to the HTTP adapter.
+%% * `http_user_agent_fragment' - Will be appended to the `user-agent` HTTP header (default: `(httpc)').
+%% * `repo_key' - Authentication key used when accessing the repository.
+%% * `repo_name' - Name of the repository, used for verifying the repository signature
+%% authenticity (default: `hexpm').
+%% * `repo_public_key' - Public key used to verify the repository signature
+%% (defaults to hexpm public key `https://hex.pm/docs/public_keys').
+%% * `repo_url' - URL to the repository (default: `https://repo.hex.pm').
+%% * `repo_organization' - Name of the organization repository, appends `/repos/:name'
+%% to the repository URL and overrides the `repo_name' option.
+%% * `repo_verify' - If `true' will verify the repository signature (default: `true').
+%% * `repo_verify_origin' - If `true' will verify the repository signature origin,
+%% requires protobuf messages as of hex_core v0.4.0 (default: `true').
+
+-module(r3_hex_core).
+-export([default_config/0]).
+
+-export_type([config/0]).
+
+%% https://hex.pm/docs/public_keys
+-define(HEXPM_PUBLIC_KEY, <<"-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApqREcFDt5vV21JVe2QNB
+Edvzk6w36aNFhVGWN5toNJRjRJ6m4hIuG4KaXtDWVLjnvct6MYMfqhC79HAGwyF+
+IqR6Q6a5bbFSsImgBJwz1oadoVKD6ZNetAuCIK84cjMrEFRkELtEIPNHblCzUkkM
+3rS9+DPlnfG8hBvGi6tvQIuZmXGCxF/73hU0/MyGhbmEjIKRtG6b0sJYKelRLTPW
+XgK7s5pESgiwf2YC/2MGDXjAJfpfCd0RpLdvd4eRiXtVlE9qO9bND94E7PgQ/xqZ
+J1i2xWFndWa6nfFnRxZmCStCOZWYYPlaxr+FZceFbpMwzTNs4g3d4tLNUcbKAIH4
+0wIDAQAB
+-----END PUBLIC KEY-----">>).
+
+
+-type config() :: #{
+ api_key => binary() | undefined,
+ api_organization => binary() | undefined,
+ api_repository => binary() | undefined,
+ api_url => binary(),
+ http_adapter => module(),
+ http_etag => binary() | undefined,
+ http_adapter_config => map(),
+ http_user_agent_fragment => binary(),
+ repo_key => binary() | undefined,
+ repo_name => binary(),
+ repo_public_key => binary(),
+ repo_url => binary(),
+ repo_organization => binary() | undefined,
+ repo_verify => boolean(),
+ repo_verify_origin => boolean()
+}.
+
+-spec default_config() -> config().
+default_config() ->
+ #{
+ api_key => undefined,
+ api_organization => undefined,
+ api_repository => undefined,
+ api_url => <<"https://hex.pm/api">>,
+ http_adapter => r3_hex_http_httpc,
+ http_adapter_config => #{profile => default},
+ http_etag => undefined,
+ http_user_agent_fragment => <<"(httpc)">>,
+ repo_key => undefined,
+ repo_name => <<"hexpm">>,
+ repo_public_key => ?HEXPM_PUBLIC_KEY,
+ repo_url => <<"https://repo.hex.pm">>,
+ repo_organization => undefined,
+ repo_verify => true,
+ repo_verify_origin => true
+ }.
diff --git a/src/r3_hex_core.hrl b/src/r3_hex_core.hrl
new file mode 100644
index 0000000..31c2bf9
--- /dev/null
+++ b/src/r3_hex_core.hrl
@@ -0,0 +1,3 @@
+%% Vendored from hex_core v0.5.0, do not edit manually
+
+-define(HEX_CORE_VERSION, "0.5.0").
diff --git a/src/r3_hex_erl_tar.erl b/src/r3_hex_erl_tar.erl
new file mode 100644
index 0000000..a698e43
--- /dev/null
+++ b/src/r3_hex_erl_tar.erl
@@ -0,0 +1,1966 @@
+%% Vendored from hex_core v0.5.0, do not edit manually
+
+%% @private
+%% Copied from https://github.com/erlang/otp/blob/OTP-20.0.1/lib/stdlib/src/erl_tar.erl
+%% with modifications:
+%% - Change module name to `r3_hex_erl_tar`
+%% - Set tar mtimes to 0 and remove dependency on :os.system_time/1
+%% - Preserve modes when building tarball
+%% - Do not crash if failing to write tar
+%% - Allow setting file_info opts on :r3_hex_erl_tar.add
+
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%% This module implements extraction/creation of tar archives.
+%% It supports reading most common tar formats, namely V7, STAR,
+%% USTAR, GNU, BSD/libarchive, and PAX. It produces archives in USTAR
+%% format, unless it must use PAX headers, in which case it produces PAX
+%% format.
+%%
+%% The following references where used:
+%% http://www.freebsd.org/cgi/man.cgi?query=tar&sektion=5
+%% http://www.gnu.org/software/tar/manual/html_node/Standard.html
+%% http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html
+-module(r3_hex_erl_tar).
+
+-export([init/3,
+ create/2, create/3,
+ extract/1, extract/2,
+ table/1, table/2, t/1, tt/1,
+ open/2, close/1,
+ add/3, add/4, add/5,
+ format_error/1]).
+
+-include_lib("kernel/include/file.hrl").
+-include_lib("r3_hex_erl_tar.hrl").
+
+%% Converts the short error reason to a descriptive string.
+-spec format_error(term()) -> string().
+format_error(invalid_tar_checksum) ->
+ "Checksum failed";
+format_error(bad_header) ->
+ "Unrecognized tar header format";
+format_error({bad_header, Reason}) ->
+ lists:flatten(io_lib:format("Unrecognized tar header format: ~p", [Reason]));
+format_error({invalid_header, negative_size}) ->
+ "Invalid header: negative size";
+format_error(invalid_sparse_header_size) ->
+ "Invalid sparse header: negative size";
+format_error(invalid_sparse_map_entry) ->
+ "Invalid sparse map entry";
+format_error({invalid_sparse_map_entry, Reason}) ->
+ lists:flatten(io_lib:format("Invalid sparse map entry: ~p", [Reason]));
+format_error(invalid_end_of_archive) ->
+ "Invalid end of archive";
+format_error(eof) ->
+ "Unexpected end of file";
+format_error(integer_overflow) ->
+ "Failed to parse numeric: integer overflow";
+format_error({misaligned_read, Pos}) ->
+ lists:flatten(io_lib:format("Read a block which was misaligned: block_size=~p pos=~p",
+ [?BLOCK_SIZE, Pos]));
+format_error(invalid_gnu_1_0_sparsemap) ->
+ "Invalid GNU sparse map (version 1.0)";
+format_error({invalid_gnu_0_1_sparsemap, Format}) ->
+ lists:flatten(io_lib:format("Invalid GNU sparse map (version ~s)", [Format]));
+format_error(unsafe_path) ->
+ "The path points above the current working directory";
+format_error({Name,Reason}) ->
+ lists:flatten(io_lib:format("~ts: ~ts", [Name,format_error(Reason)]));
+format_error(Atom) when is_atom(Atom) ->
+ file:format_error(Atom);
+format_error(Term) ->
+ lists:flatten(io_lib:format("~tp", [Term])).
+
+%% Initializes a new reader given a custom file handle and I/O wrappers
+-spec init(handle(), write | read, file_op()) -> {ok, reader()} | {error, badarg}.
+init(Handle, AccessMode, Fun) when is_function(Fun, 2) ->
+ Reader = #reader{handle=Handle,access=AccessMode,func=Fun},
+ {ok, Pos, Reader2} = do_position(Reader, {cur, 0}),
+ {ok, Reader2#reader{pos=Pos}};
+init(_Handle, _AccessMode, _Fun) ->
+ {error, badarg}.
+
+%%%================================================================
+%% Extracts all files from the tar file Name.
+-spec extract(open_handle()) -> ok | {error, term()}.
+extract(Name) ->
+ extract(Name, []).
+
+%% Extracts (all) files from the tar file Name.
+%% Options accepted:
+%% - cooked: Opens the tar file without mode `raw`
+%% - compressed: Uncompresses the tar file when reading
+%% - memory: Returns the tar contents as a list of tuples {Name, Bin}
+%% - keep_old_files: Extracted files will not overwrite the destination
+%% - {files, ListOfFilesToExtract}: Only extract ListOfFilesToExtract
+%% - verbose: Prints verbose information about the extraction,
+%% - {cwd, AbsoluteDir}: Sets the current working directory for the extraction
+-spec extract(open_handle(), [extract_opt()]) ->
+ ok
+ | {ok, [{string(), binary()}]}
+ | {error, term()}.
+extract({binary, Bin}, Opts) when is_list(Opts) ->
+ do_extract({binary, Bin}, Opts);
+extract({file, Fd}, Opts) when is_list(Opts) ->
+ do_extract({file, Fd}, Opts);
+extract(#reader{}=Reader, Opts) when is_list(Opts) ->
+ do_extract(Reader, Opts);
+extract(Name, Opts) when is_list(Name); is_binary(Name), is_list(Opts) ->
+ do_extract(Name, Opts).
+
+do_extract(Handle, Opts) when is_list(Opts) ->
+ Opts2 = extract_opts(Opts),
+ Acc = if Opts2#read_opts.output =:= memory -> []; true -> ok end,
+ foldl_read(Handle, fun extract1/4, Acc, Opts2).
+
+extract1(eof, Reader, _, Acc) when is_list(Acc) ->
+ {ok, {ok, lists:reverse(Acc)}, Reader};
+extract1(eof, Reader, _, leading_slash) ->
+ error_logger:info_msg("erl_tar: removed leading '/' from member names\n"),
+ {ok, ok, Reader};
+extract1(eof, Reader, _, Acc) ->
+ {ok, Acc, Reader};
+extract1(#tar_header{name=Name,size=Size}=Header, Reader0, Opts, Acc0) ->
+ case check_extract(Name, Opts) of
+ true ->
+ case do_read(Reader0, Size) of
+ {ok, Bin, Reader1} ->
+ Acc = extract2(Header, Bin, Opts, Acc0),
+ {ok, Acc, Reader1};
+ {error, _} = Err ->
+ throw(Err)
+ end;
+ false ->
+ {ok, Acc0, skip_file(Reader0)}
+ end.
+
+extract2(Header, Bin, Opts, Acc) ->
+ case write_extracted_element(Header, Bin, Opts) of
+ ok ->
+ case Header of
+ #tar_header{name="/"++_} ->
+ leading_slash;
+ #tar_header{} ->
+ Acc
+ end;
+ {ok, NameBin} when is_list(Acc) ->
+ [NameBin | Acc];
+ {error, _} = Err ->
+ throw(Err)
+ end.
+
+%% Checks if the file Name should be extracted.
+check_extract(_, #read_opts{files=all}) ->
+ true;
+check_extract(Name, #read_opts{files=Files}) ->
+ ordsets:is_element(Name, Files).
+
+%%%================================================================
+%% The following table functions produce a list of information about
+%% the files contained in the archive.
+-type filename() :: string().
+-type typeflag() :: regular | link | symlink |
+ char | block | directory |
+ fifo | reserved | unknown.
+-type mode() :: non_neg_integer().
+-type uid() :: non_neg_integer().
+-type gid() :: non_neg_integer().
+
+-type tar_entry() :: {filename(),
+ typeflag(),
+ non_neg_integer(),
+ tar_time(),
+ mode(),
+ uid(),
+ gid()}.
+
+%% Returns a list of names of the files in the tar file Name.
+-spec table(open_handle()) -> {ok, [string()]} | {error, term()}.
+table(Name) ->
+ table(Name, []).
+
+%% Returns a list of names of the files in the tar file Name.
+%% Options accepted: compressed, verbose, cooked.
+-spec table(open_handle(), [compressed | verbose | cooked]) ->
+ {ok, [tar_entry()]} | {error, term()}.
+table(Name, Opts) when is_list(Opts) ->
+ foldl_read(Name, fun table1/4, [], table_opts(Opts)).
+
+table1(eof, Reader, _, Result) ->
+ {ok, {ok, lists:reverse(Result)}, Reader};
+table1(#tar_header{}=Header, Reader, #read_opts{verbose=Verbose}, Result) ->
+ Attrs = table1_attrs(Header, Verbose),
+ Reader2 = skip_file(Reader),
+ {ok, [Attrs|Result], Reader2}.
+
+%% Extracts attributes relevant to table1's output
+table1_attrs(#tar_header{typeflag=Typeflag,mode=Mode}=Header, true) ->
+ Type = typeflag(Typeflag),
+ Name = Header#tar_header.name,
+ Mtime = Header#tar_header.mtime,
+ Uid = Header#tar_header.uid,
+ Gid = Header#tar_header.gid,
+ Size = Header#tar_header.size,
+ {Name, Type, Size, Mtime, Mode, Uid, Gid};
+table1_attrs(#tar_header{name=Name}, _Verbose) ->
+ Name.
+
+typeflag(?TYPE_REGULAR) -> regular;
+typeflag(?TYPE_REGULAR_A) -> regular;
+typeflag(?TYPE_GNU_SPARSE) -> regular;
+typeflag(?TYPE_CONT) -> regular;
+typeflag(?TYPE_LINK) -> link;
+typeflag(?TYPE_SYMLINK) -> symlink;
+typeflag(?TYPE_CHAR) -> char;
+typeflag(?TYPE_BLOCK) -> block;
+typeflag(?TYPE_DIR) -> directory;
+typeflag(?TYPE_FIFO) -> fifo;
+typeflag(_) -> unknown.
+
+%%%================================================================
+%% Comments for printing the contents of a tape archive,
+%% meant to be invoked from the shell.
+
+%% Prints each filename in the archive
+-spec t(file:filename()) -> ok | {error, term()}.
+t(Name) when is_list(Name); is_binary(Name) ->
+ case table(Name) of
+ {ok, List} ->
+ lists:foreach(fun(N) -> ok = io:format("~ts\n", [N]) end, List);
+ Error ->
+ Error
+ end.
+
+%% Prints verbose information about each file in the archive
+-spec tt(open_handle()) -> ok | {error, term()}.
+tt(Name) ->
+ case table(Name, [verbose]) of
+ {ok, List} ->
+ lists:foreach(fun print_header/1, List);
+ Error ->
+ Error
+ end.
+
+%% Used by tt/1 to print a tar_entry tuple
+-spec print_header(tar_entry()) -> ok.
+print_header({Name, Type, Size, Mtime, Mode, Uid, Gid}) ->
+ io:format("~s~s ~4w/~-4w ~7w ~s ~s\n",
+ [type_to_string(Type), mode_to_string(Mode),
+ Uid, Gid, Size, time_to_string(Mtime), Name]).
+
+type_to_string(regular) -> "-";
+type_to_string(directory) -> "d";
+type_to_string(link) -> "l";
+type_to_string(symlink) -> "s";
+type_to_string(char) -> "c";
+type_to_string(block) -> "b";
+type_to_string(fifo) -> "f";
+type_to_string(unknown) -> "?".
+
+%% Converts a numeric mode to its human-readable representation
+mode_to_string(Mode) ->
+ mode_to_string(Mode, "xwrxwrxwr", []).
+mode_to_string(Mode, [C|T], Acc) when Mode band 1 =:= 1 ->
+ mode_to_string(Mode bsr 1, T, [C|Acc]);
+mode_to_string(Mode, [_|T], Acc) ->
+ mode_to_string(Mode bsr 1, T, [$-|Acc]);
+mode_to_string(_, [], Acc) ->
+ Acc.
+
+%% Converts a tar_time() (POSIX time) to a readable string
+time_to_string(Secs0) ->
+ Epoch = calendar:datetime_to_gregorian_seconds(?EPOCH),
+ Secs = Epoch + Secs0,
+ DateTime0 = calendar:gregorian_seconds_to_datetime(Secs),
+ DateTime = calendar:universal_time_to_local_time(DateTime0),
+ {{Y, Mon, Day}, {H, Min, _}} = DateTime,
+ io_lib:format("~s ~2w ~s:~s ~w", [month(Mon), Day, two_d(H), two_d(Min), Y]).
+
+two_d(N) ->
+ tl(integer_to_list(N + 100)).
+
+month(1) -> "Jan";
+month(2) -> "Feb";
+month(3) -> "Mar";
+month(4) -> "Apr";
+month(5) -> "May";
+month(6) -> "Jun";
+month(7) -> "Jul";
+month(8) -> "Aug";
+month(9) -> "Sep";
+month(10) -> "Oct";
+month(11) -> "Nov";
+month(12) -> "Dec".
+
+%%%================================================================
+%% The open function with friends is to keep the file and binary api of this module
+-type open_handle() :: file:filename()
+ | {binary, binary()}
+ | {file, term()}.
+-spec open(open_handle(), [write | compressed | cooked]) ->
+ {ok, reader()} | {error, term()}.
+open({binary, Bin}, Mode) when is_binary(Bin) ->
+ do_open({binary, Bin}, Mode);
+open({file, Fd}, Mode) ->
+ do_open({file, Fd}, Mode);
+open(Name, Mode) when is_list(Name); is_binary(Name) ->
+ do_open(Name, Mode).
+
+do_open(Name, Mode) when is_list(Mode) ->
+ case open_mode(Mode) of
+ {ok, Access, Raw, Opts} ->
+ open1(Name, Access, Raw, Opts);
+ {error, Reason} ->
+ {error, {Name, Reason}}
+ end.
+
+open1({binary,Bin}, read, _Raw, Opts) when is_binary(Bin) ->
+ case file:open(Bin, [ram,binary,read]) of
+ {ok,File} ->
+ _ = [ram_file:uncompress(File) || Opts =:= [compressed]],
+ {ok, #reader{handle=File,access=read,func=fun file_op/2}};
+ Error ->
+ Error
+ end;
+open1({file, Fd}, read, _Raw, _Opts) ->
+ Reader = #reader{handle=Fd,access=read,func=fun file_op/2},
+ case do_position(Reader, {cur, 0}) of
+ {ok, Pos, Reader2} ->
+ {ok, Reader2#reader{pos=Pos}};
+ {error, _} = Err ->
+ Err
+ end;
+open1(Name, Access, Raw, Opts) when is_list(Name) or is_binary(Name) ->
+ case file:open(Name, Raw ++ [binary, Access|Opts]) of
+ {ok, File} ->
+ {ok, #reader{handle=File,access=Access,func=fun file_op/2}};
+ {error, Reason} ->
+ {error, {Name, Reason}}
+ end.
+
+open_mode(Mode) ->
+ open_mode(Mode, false, [raw], []).
+
+open_mode(read, _, Raw, _) ->
+ {ok, read, Raw, []};
+open_mode(write, _, Raw, _) ->
+ {ok, write, Raw, []};
+open_mode([read|Rest], false, Raw, Opts) ->
+ open_mode(Rest, read, Raw, Opts);
+open_mode([write|Rest], false, Raw, Opts) ->
+ open_mode(Rest, write, Raw, Opts);
+open_mode([compressed|Rest], Access, Raw, Opts) ->
+ open_mode(Rest, Access, Raw, [compressed|Opts]);
+open_mode([cooked|Rest], Access, _Raw, Opts) ->
+ open_mode(Rest, Access, [], Opts);
+open_mode([], Access, Raw, Opts) ->
+ {ok, Access, Raw, Opts};
+open_mode(_, _, _, _) ->
+ {error, einval}.
+
+file_op(write, {Fd, Data}) ->
+ file:write(Fd, Data);
+file_op(position, {Fd, Pos}) ->
+ file:position(Fd, Pos);
+file_op(read2, {Fd, Size}) ->
+ file:read(Fd, Size);
+file_op(close, Fd) ->
+ file:close(Fd).
+
+%% Closes a tar archive.
+-spec close(reader()) -> ok | {error, term()}.
+close(#reader{access=read}=Reader) ->
+ ok = do_close(Reader);
+close(#reader{access=write}=Reader) ->
+ {ok, Reader2} = pad_file(Reader),
+ ok = do_close(Reader2),
+ ok;
+close(_) ->
+ {error, einval}.
+
+pad_file(#reader{pos=Pos}=Reader) ->
+ %% There must be at least two zero blocks at the end.
+ PadCurrent = skip_padding(Pos+?BLOCK_SIZE),
+ Padding = <<0:PadCurrent/unit:8>>,
+ do_write(Reader, [Padding, ?ZERO_BLOCK, ?ZERO_BLOCK]).
+
+
+%%%================================================================
+%% Creation/modification of tar archives
+
+%% Creates a tar file Name containing the given files.
+-spec create(file:filename(), filelist()) -> ok | {error, {string(), term()}}.
+create(Name, FileList) when is_list(Name); is_binary(Name) ->
+ create(Name, FileList, []).
+
+%% Creates a tar archive Name containing the given files.
+%% Accepted options: verbose, compressed, cooked
+-spec create(file:filename(), filelist(), [create_opt()]) ->
+ ok | {error, term()} | {error, {string(), term()}}.
+create(Name, FileList, Options) when is_list(Name); is_binary(Name) ->
+ Mode = lists:filter(fun(X) -> (X=:=compressed) or (X=:=cooked)
+ end, Options),
+ case open(Name, [write|Mode]) of
+ {ok, TarFile} ->
+ do_create(TarFile, FileList, Options);
+ {error, _} = Err ->
+ Err
+ end.
+
+do_create(TarFile, [], _Opts) ->
+ close(TarFile);
+do_create(TarFile, [{NameInArchive, NameOrBin}|Rest], Opts) ->
+ case add(TarFile, NameOrBin, NameInArchive, Opts) of
+ ok ->
+ do_create(TarFile, Rest, Opts);
+ {error, _} = Err ->
+ _ = close(TarFile),
+ Err
+ end;
+do_create(TarFile, [Name|Rest], Opts) ->
+ case add(TarFile, Name, Name, Opts) of
+ ok ->
+ do_create(TarFile, Rest, Opts);
+ {error, _} = Err ->
+ _ = close(TarFile),
+ Err
+ end.
+
+%% Adds a file to a tape archive.
+-type add_type() :: string()
+ | {string(), string()}
+ | {string(), binary()}.
+-spec add(reader(), add_type(), [add_opt()]) -> ok | {error, term()}.
+add(Reader, {NameInArchive, Name}, Opts)
+ when is_list(NameInArchive), is_list(Name) ->
+ do_add(Reader, Name, NameInArchive, undefined, Opts);
+add(Reader, {NameInArchive, Bin}, Opts)
+ when is_list(NameInArchive), is_binary(Bin) ->
+ do_add(Reader, Bin, NameInArchive, undefined, Opts);
+add(Reader, {NameInArchive, Bin, Mode}, Opts)
+ when is_list(NameInArchive), is_binary(Bin), is_integer(Mode) ->
+ do_add(Reader, Bin, NameInArchive, Mode, Opts);
+add(Reader, Name, Opts) when is_list(Name) ->
+ do_add(Reader, Name, Name, undefined, Opts).
+
+
+-spec add(reader(), string() | binary(), string(), [add_opt()]) ->
+ ok | {error, term()}.
+add(Reader, NameOrBin, NameInArchive, Options)
+ when is_list(NameOrBin); is_binary(NameOrBin),
+ is_list(NameInArchive), is_list(Options) ->
+ do_add(Reader, NameOrBin, NameInArchive, undefined, Options).
+
+-spec add(reader(), string() | binary(), string(), integer(), [add_opt()]) ->
+ ok | {error, term()}.
+add(Reader, NameOrBin, NameInArchive, Mode, Options)
+ when is_list(NameOrBin); is_binary(NameOrBin),
+ is_list(NameInArchive), is_integer(Mode), is_list(Options) ->
+ do_add(Reader, NameOrBin, NameInArchive, Mode, Options).
+
+do_add(#reader{access=write}=Reader, Name, NameInArchive, Mode, Options)
+ when is_list(NameInArchive), is_list(Options) ->
+ RF = fun(F) -> apply_file_info_opts(Options, file:read_link_info(F, [{time, posix}])) end,
+ Opts = #add_opts{read_info=RF},
+ add1(Reader, Name, NameInArchive, Mode, add_opts(Options, Options, Opts));
+do_add(#reader{access=read},_,_,_,_) ->
+ {error, eacces};
+do_add(Reader,_,_,_,_) ->
+ {error, {badarg, Reader}}.
+
+add_opts([dereference|T], AllOptions, Opts) ->
+ RF = fun(F) -> apply_file_info_opts(AllOptions, file:read_file_info(F, [{time, posix}])) end,
+ add_opts(T, AllOptions, Opts#add_opts{read_info=RF});
+add_opts([verbose|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts#add_opts{verbose=true});
+add_opts([{chunks,N}|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts#add_opts{chunk_size=N});
+add_opts([{atime,Value}|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts#add_opts{atime=Value});
+add_opts([{mtime,Value}|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts#add_opts{mtime=Value});
+add_opts([{ctime,Value}|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts#add_opts{ctime=Value});
+add_opts([{uid,Value}|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts#add_opts{uid=Value});
+add_opts([{gid,Value}|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts#add_opts{gid=Value});
+add_opts([_|T], AllOptions, Opts) ->
+ add_opts(T, AllOptions, Opts);
+add_opts([], _AllOptions, Opts) ->
+ Opts.
+
+apply_file_info_opts(Opts, {ok, FileInfo}) ->
+ {ok, do_apply_file_info_opts(Opts, FileInfo)};
+apply_file_info_opts(_Opts, Other) ->
+ Other.
+
+do_apply_file_info_opts([{atime,Value}|T], FileInfo) ->
+ do_apply_file_info_opts(T, FileInfo#file_info{atime=Value});
+do_apply_file_info_opts([{mtime,Value}|T], FileInfo) ->
+ do_apply_file_info_opts(T, FileInfo#file_info{mtime=Value});
+do_apply_file_info_opts([{ctime,Value}|T], FileInfo) ->
+ do_apply_file_info_opts(T, FileInfo#file_info{ctime=Value});
+do_apply_file_info_opts([{uid,Value}|T], FileInfo) ->
+ do_apply_file_info_opts(T, FileInfo#file_info{uid=Value});
+do_apply_file_info_opts([{gid,Value}|T], FileInfo) ->
+ do_apply_file_info_opts(T, FileInfo#file_info{gid=Value});
+do_apply_file_info_opts([_|T], FileInfo) ->
+ do_apply_file_info_opts(T, FileInfo);
+do_apply_file_info_opts([], FileInfo) ->
+ FileInfo.
+
+add1(#reader{}=Reader, Name, NameInArchive, undefined, #add_opts{read_info=ReadInfo}=Opts)
+ when is_list(Name) ->
+ Res = case ReadInfo(Name) of
+ {error, Reason0} ->
+ {error, {Name, Reason0}};
+ {ok, #file_info{type=symlink}=Fi} ->
+ add_verbose(Opts, "a ~ts~n", [NameInArchive]),
+ {ok, Linkname} = file:read_link(Name),
+ Header = fileinfo_to_header(NameInArchive, Fi, Linkname),
+ add_header(Reader, Header, Opts);
+ {ok, #file_info{type=regular}=Fi} ->
+ add_verbose(Opts, "a ~ts~n", [NameInArchive]),
+ Header = fileinfo_to_header(NameInArchive, Fi, false),
+ {ok, Reader2} = add_header(Reader, Header, Opts),
+ FileSize = Header#tar_header.size,
+ {ok, FileSize, Reader3} = do_copy(Reader2, Name, Opts),
+ Padding = skip_padding(FileSize),
+ Pad = <<0:Padding/unit:8>>,
+ do_write(Reader3, Pad);
+ {ok, #file_info{type=directory}=Fi} ->
+ add_directory(Reader, Name, NameInArchive, Fi, Opts);
+ {ok, #file_info{}=Fi} ->
+ add_verbose(Opts, "a ~ts~n", [NameInArchive]),
+ Header = fileinfo_to_header(NameInArchive, Fi, false),
+ add_header(Reader, Header, Opts)
+ end,
+ case Res of
+ ok -> ok;
+ {ok, _Reader} -> ok;
+ {error, _Reason} = Err -> Err
+ end;
+add1(Reader, Bin, NameInArchive, Mode, Opts) when is_binary(Bin) ->
+ add_verbose(Opts, "a ~ts~n", [NameInArchive]),
+ Now = 0,
+ Header = #tar_header{
+ name = NameInArchive,
+ size = byte_size(Bin),
+ typeflag = ?TYPE_REGULAR,
+ atime = add_opts_time(Opts#add_opts.atime, Now),
+ mtime = add_opts_time(Opts#add_opts.mtime, Now),
+ ctime = add_opts_time(Opts#add_opts.ctime, Now),
+ uid = Opts#add_opts.uid,
+ gid = Opts#add_opts.gid,
+ mode = default_mode(Mode, 8#100644)},
+ {ok, Reader2} = add_header(Reader, Header, Opts),
+ Padding = skip_padding(byte_size(Bin)),
+ Data = [Bin, <<0:Padding/unit:8>>],
+ case do_write(Reader2, Data) of
+ {ok, _Reader3} -> ok;
+ {error, Reason} -> {error, {NameInArchive, Reason}}
+ end.
+
+add_opts_time(undefined, _Now) -> 0;
+add_opts_time(Time, _Now) -> Time.
+
+default_mode(undefined, Mode) -> Mode;
+default_mode(Mode, _) -> Mode.
+
+add_directory(Reader, DirName, NameInArchive, Info, Opts) ->
+ case file:list_dir(DirName) of
+ {ok, []} ->
+ add_verbose(Opts, "a ~ts~n", [NameInArchive]),
+ Header = fileinfo_to_header(NameInArchive, Info, false),
+ add_header(Reader, Header, Opts);
+ {ok, Files} ->
+ add_verbose(Opts, "a ~ts~n", [NameInArchive]),
+ try add_files(Reader, Files, DirName, NameInArchive, Opts) of
+ ok -> ok;
+ {error, _} = Err -> Err
+ catch
+ throw:{error, {_Name, _Reason}} = Err -> Err;
+ throw:{error, Reason} -> {error, {DirName, Reason}}
+ end;
+ {error, Reason} ->
+ {error, {DirName, Reason}}
+ end.
+
+add_files(_Reader, [], _Dir, _DirInArchive, _Opts) ->
+ ok;
+add_files(Reader, [Name|Rest], Dir, DirInArchive, #add_opts{read_info=Info}=Opts) ->
+ FullName = filename:join(Dir, Name),
+ NameInArchive = filename:join(DirInArchive, Name),
+ Res = case Info(FullName) of
+ {error, Reason} ->
+ {error, {FullName, Reason}};
+ {ok, #file_info{type=directory}=Fi} ->
+ add_directory(Reader, FullName, NameInArchive, Fi, Opts);
+ {ok, #file_info{type=symlink}=Fi} ->
+ add_verbose(Opts, "a ~ts~n", [NameInArchive]),
+ {ok, Linkname} = file:read_link(FullName),
+ Header = fileinfo_to_header(NameInArchive, Fi, Linkname),
+ add_header(Reader, Header, Opts);
+ {ok, #file_info{type=regular}=Fi} ->
+ add_verbose(Opts, "a ~ts~n", [NameInArchive]),
+ Header = fileinfo_to_header(NameInArchive, Fi, false),
+ {ok, Reader2} = add_header(Reader, Header, Opts),
+ FileSize = Header#tar_header.size,
+ {ok, FileSize, Reader3} = do_copy(Reader2, FullName, Opts),
+ Padding = skip_padding(FileSize),
+ Pad = <<0:Padding/unit:8>>,
+ do_write(Reader3, Pad);
+ {ok, #file_info{}=Fi} ->
+ add_verbose(Opts, "a ~ts~n", [NameInArchive]),
+ Header = fileinfo_to_header(NameInArchive, Fi, false),
+ add_header(Reader, Header, Opts)
+ end,
+ case Res of
+ ok -> add_files(Reader, Rest, Dir, DirInArchive, Opts);
+ {ok, ReaderNext} -> add_files(ReaderNext, Rest, Dir, DirInArchive, Opts);
+ {error, _} = Err -> Err
+ end.
+
+format_string(String, Size) when length(String) > Size ->
+ throw({error, {write_string, field_too_long}});
+format_string(String, Size) ->
+ Ascii = to_ascii(String),
+ if byte_size(Ascii) < Size ->
+ [Ascii, 0];
+ true ->
+ Ascii
+ end.
+
+format_octal(Octal) ->
+ iolist_to_binary(io_lib:fwrite("~.8B", [Octal])).
+
+add_header(#reader{}=Reader, #tar_header{}=Header, Opts) ->
+ {ok, Iodata} = build_header(Header, Opts),
+ do_write(Reader, Iodata).
+
+write_to_block(Block, IoData, Start) when is_list(IoData) ->
+ write_to_block(Block, iolist_to_binary(IoData), Start);
+write_to_block(Block, Bin, Start) when is_binary(Bin) ->
+ Size = byte_size(Bin),
+ <<Head:Start/unit:8, _:Size/unit:8, Rest/binary>> = Block,
+ <<Head:Start/unit:8, Bin/binary, Rest/binary>>.
+
+build_header(#tar_header{}=Header, Opts) ->
+ #tar_header{
+ name=Name,
+ mode=Mode,
+ uid=Uid,
+ gid=Gid,
+ size=Size,
+ typeflag=Type,
+ linkname=Linkname,
+ uname=Uname,
+ gname=Gname,
+ devmajor=Devmaj,
+ devminor=Devmin
+ } = Header,
+ Mtime = Header#tar_header.mtime,
+
+ Block0 = ?ZERO_BLOCK,
+ {Block1, Pax0} = write_string(Block0, ?V7_NAME, ?V7_NAME_LEN, Name, ?PAX_PATH, #{}),
+ Block2 = write_octal(Block1, ?V7_MODE, ?V7_MODE_LEN, Mode),
+ {Block3, Pax1} = write_numeric(Block2, ?V7_UID, ?V7_UID_LEN, Uid, ?PAX_UID, Pax0),
+ {Block4, Pax2} = write_numeric(Block3, ?V7_GID, ?V7_GID_LEN, Gid, ?PAX_GID, Pax1),
+ {Block5, Pax3} = write_numeric(Block4, ?V7_SIZE, ?V7_SIZE_LEN, Size, ?PAX_SIZE, Pax2),
+ {Block6, Pax4} = write_numeric(Block5, ?V7_MTIME, ?V7_MTIME_LEN, Mtime, ?PAX_NONE, Pax3),
+ {Block7, Pax5} = write_string(Block6, ?V7_TYPE, ?V7_TYPE_LEN, <<Type>>, ?PAX_NONE, Pax4),
+ {Block8, Pax6} = write_string(Block7, ?V7_LINKNAME, ?V7_LINKNAME_LEN,
+ Linkname, ?PAX_LINKPATH, Pax5),
+ {Block9, Pax7} = write_string(Block8, ?USTAR_UNAME, ?USTAR_UNAME_LEN,
+ Uname, ?PAX_UNAME, Pax6),
+ {Block10, Pax8} = write_string(Block9, ?USTAR_GNAME, ?USTAR_GNAME_LEN,
+ Gname, ?PAX_GNAME, Pax7),
+ {Block11, Pax9} = write_numeric(Block10, ?USTAR_DEVMAJ, ?USTAR_DEVMAJ_LEN,
+ Devmaj, ?PAX_NONE, Pax8),
+ {Block12, Pax10} = write_numeric(Block11, ?USTAR_DEVMIN, ?USTAR_DEVMIN_LEN,
+ Devmin, ?PAX_NONE, Pax9),
+ {Block13, Pax11} = set_path(Block12, Pax10),
+ PaxEntry = case maps:size(Pax11) of
+ 0 -> [];
+ _ -> build_pax_entry(Header, Pax11, Opts)
+ end,
+ Block14 = set_format(Block13, ?FORMAT_USTAR),
+ Block15 = set_checksum(Block14),
+ {ok, [PaxEntry, Block15]}.
+
+set_path(Block0, Pax) ->
+ %% only use ustar header when name is too long
+ case maps:get(?PAX_PATH, Pax, nil) of
+ nil ->
+ {Block0, Pax};
+ PaxPath ->
+ case split_ustar_path(PaxPath) of
+ {ok, UstarName, UstarPrefix} ->
+ {Block1, _} = write_string(Block0, ?V7_NAME, ?V7_NAME_LEN,
+ UstarName, ?PAX_NONE, #{}),
+ {Block2, _} = write_string(Block1, ?USTAR_PREFIX, ?USTAR_PREFIX_LEN,
+ UstarPrefix, ?PAX_NONE, #{}),
+ {Block2, maps:remove(?PAX_PATH, Pax)};
+ false ->
+ {Block0, Pax}
+ end
+ end.
+
+set_format(Block0, Format)
+ when Format =:= ?FORMAT_USTAR; Format =:= ?FORMAT_PAX ->
+ Block1 = write_to_block(Block0, ?MAGIC_USTAR, ?USTAR_MAGIC),
+ write_to_block(Block1, ?VERSION_USTAR, ?USTAR_VERSION);
+set_format(_Block, Format) ->
+ throw({error, {invalid_format, Format}}).
+
+set_checksum(Block) ->
+ Checksum = compute_checksum(Block),
+ write_octal(Block, ?V7_CHKSUM, ?V7_CHKSUM_LEN, Checksum).
+
+build_pax_entry(Header, PaxAttrs, Opts) ->
+ Path = Header#tar_header.name,
+ Filename = filename:basename(Path),
+ Dir = filename:dirname(Path),
+ Path2 = filename:join([Dir, "PaxHeaders.0", Filename]),
+ AsciiPath = to_ascii(Path2),
+ Path3 = if byte_size(AsciiPath) > ?V7_NAME_LEN ->
+ binary_part(AsciiPath, 0, ?V7_NAME_LEN - 1);
+ true ->
+ AsciiPath
+ end,
+ Keys = maps:keys(PaxAttrs),
+ SortedKeys = lists:sort(Keys),
+ PaxFile = build_pax_file(SortedKeys, PaxAttrs),
+ Size = byte_size(PaxFile),
+ Padding = (?BLOCK_SIZE -
+ (byte_size(PaxFile) rem ?BLOCK_SIZE)) rem ?BLOCK_SIZE,
+ Pad = <<0:Padding/unit:8>>,
+ PaxHeader = #tar_header{
+ name=unicode:characters_to_list(Path3),
+ size=Size,
+ mtime=Header#tar_header.mtime,
+ atime=Header#tar_header.atime,
+ ctime=Header#tar_header.ctime,
+ typeflag=?TYPE_X_HEADER
+ },
+ {ok, PaxHeaderData} = build_header(PaxHeader, Opts),
+ [PaxHeaderData, PaxFile, Pad].
+
+build_pax_file(Keys, PaxAttrs) ->
+ build_pax_file(Keys, PaxAttrs, []).
+build_pax_file([], _, Acc) ->
+ unicode:characters_to_binary(Acc);
+build_pax_file([K|Rest], Attrs, Acc) ->
+ V = maps:get(K, Attrs),
+ Size = sizeof(K) + sizeof(V) + 3,
+ Size2 = sizeof(Size) + Size,
+ Key = to_string(K),
+ Value = to_string(V),
+ Record = unicode:characters_to_binary(io_lib:format("~B ~ts=~ts\n", [Size2, Key, Value])),
+ if byte_size(Record) =/= Size2 ->
+ Size3 = byte_size(Record),
+ Record2 = io_lib:format("~B ~ts=~ts\n", [Size3, Key, Value]),
+ build_pax_file(Rest, Attrs, [Acc, Record2]);
+ true ->
+ build_pax_file(Rest, Attrs, [Acc, Record])
+ end.
+
+sizeof(Bin) when is_binary(Bin) ->
+ byte_size(Bin);
+sizeof(List) when is_list(List) ->
+ length(List);
+sizeof(N) when is_integer(N) ->
+ byte_size(integer_to_binary(N));
+sizeof(N) when is_float(N) ->
+ byte_size(float_to_binary(N)).
+
+to_string(Bin) when is_binary(Bin) ->
+ unicode:characters_to_list(Bin);
+to_string(List) when is_list(List) ->
+ List;
+to_string(N) when is_integer(N) ->
+ integer_to_list(N);
+to_string(N) when is_float(N) ->
+ float_to_list(N).
+
+split_ustar_path(Path) ->
+ Len = length(Path),
+ NotAscii = not is_ascii(Path),
+ if Len =< ?V7_NAME_LEN; NotAscii ->
+ false;
+ true ->
+ PathBin = binary:list_to_bin(Path),
+ case binary:split(PathBin, [<<$/>>], [global, trim_all]) of
+ [Part] when byte_size(Part) >= ?V7_NAME_LEN ->
+ false;
+ Parts ->
+ case lists:last(Parts) of
+ Name when byte_size(Name) >= ?V7_NAME_LEN ->
+ false;
+ Name ->
+ Parts2 = lists:sublist(Parts, length(Parts) - 1),
+ join_split_ustar_path(Parts2, {ok, Name, nil})
+ end
+ end
+ end.
+
+join_split_ustar_path([], Acc) ->
+ Acc;
+join_split_ustar_path([Part|_], {ok, _, nil})
+ when byte_size(Part) > ?USTAR_PREFIX_LEN ->
+ false;
+join_split_ustar_path([Part|_], {ok, _Name, Acc})
+ when (byte_size(Part)+byte_size(Acc)) > ?USTAR_PREFIX_LEN ->
+ false;
+join_split_ustar_path([Part|Rest], {ok, Name, nil}) ->
+ join_split_ustar_path(Rest, {ok, Name, Part});
+join_split_ustar_path([Part|Rest], {ok, Name, Acc}) ->
+ join_split_ustar_path(Rest, {ok, Name, <<Acc/binary,$/,Part/binary>>}).
+
+write_octal(Block, Pos, Size, X) ->
+ Octal = zero_pad(format_octal(X), Size-1),
+ if byte_size(Octal) < Size ->
+ write_to_block(Block, Octal, Pos);
+ true ->
+ throw({error, {write_failed, octal_field_too_long}})
+ end.
+
+write_string(Block, Pos, Size, Str, PaxAttr, Pax0) ->
+ NotAscii = not is_ascii(Str),
+ if PaxAttr =/= ?PAX_NONE andalso (length(Str) > Size orelse NotAscii) ->
+ Pax1 = maps:put(PaxAttr, Str, Pax0),
+ {Block, Pax1};
+ true ->
+ Formatted = format_string(Str, Size),
+ {write_to_block(Block, Formatted, Pos), Pax0}
+ end.
+write_numeric(Block, Pos, Size, X, PaxAttr, Pax0) ->
+ %% attempt octal
+ Octal = zero_pad(format_octal(X), Size-1),
+ if byte_size(Octal) < Size ->
+ {write_to_block(Block, [Octal, 0], Pos), Pax0};
+ PaxAttr =/= ?PAX_NONE ->
+ Pax1 = maps:put(PaxAttr, X, Pax0),
+ {Block, Pax1};
+ true ->
+ throw({error, {write_failed, numeric_field_too_long}})
+ end.
+
+zero_pad(Str, Size) when byte_size(Str) >= Size ->
+ Str;
+zero_pad(Str, Size) ->
+ Padding = Size - byte_size(Str),
+ Pad = binary:copy(<<$0>>, Padding),
+ <<Pad/binary, Str/binary>>.
+
+
+%%%================================================================
+%% Functions for creating or modifying tar archives
+
+read_block(Reader) ->
+ case do_read(Reader, ?BLOCK_SIZE) of
+ eof ->
+ throw({error, eof});
+ %% Two zero blocks mark the end of the archive
+ {ok, ?ZERO_BLOCK, Reader1} ->
+ case do_read(Reader1, ?BLOCK_SIZE) of
+ eof ->
+ % This is technically a malformed end-of-archive marker,
+ % as two ZERO_BLOCKs are expected as the marker,
+ % but if we've already made it this far, we should just ignore it
+ eof;
+ {ok, ?ZERO_BLOCK, _Reader2} ->
+ eof;
+ {ok, _Block, _Reader2} ->
+ throw({error, invalid_end_of_archive});
+ {error,_} = Err ->
+ throw(Err)
+ end;
+ {ok, Block, Reader1} when is_binary(Block) ->
+ {ok, Block, Reader1};
+ {error, _} = Err ->
+ throw(Err)
+ end.
+
+get_header(#reader{}=Reader) ->
+ case read_block(Reader) of
+ eof ->
+ eof;
+ {ok, Block, Reader1} ->
+ convert_header(Block, Reader1)
+ end.
+
+%% Converts the tar header to a record.
+to_v7(Bin) when is_binary(Bin), byte_size(Bin) =:= ?BLOCK_SIZE ->
+ #header_v7{
+ name=binary_part(Bin, ?V7_NAME, ?V7_NAME_LEN),
+ mode=binary_part(Bin, ?V7_MODE, ?V7_MODE_LEN),
+ uid=binary_part(Bin, ?V7_UID, ?V7_UID_LEN),
+ gid=binary_part(Bin, ?V7_GID, ?V7_GID_LEN),
+ size=binary_part(Bin, ?V7_SIZE, ?V7_SIZE_LEN),
+ mtime=binary_part(Bin, ?V7_MTIME, ?V7_MTIME_LEN),
+ checksum=binary_part(Bin, ?V7_CHKSUM, ?V7_CHKSUM_LEN),
+ typeflag=binary:at(Bin, ?V7_TYPE),
+ linkname=binary_part(Bin, ?V7_LINKNAME, ?V7_LINKNAME_LEN)
+ };
+to_v7(_) ->
+ {error, header_block_too_small}.
+
+to_gnu(#header_v7{}=V7, Bin)
+ when is_binary(Bin), byte_size(Bin) =:= ?BLOCK_SIZE ->
+ #header_gnu{
+ header_v7=V7,
+ magic=binary_part(Bin, ?GNU_MAGIC, ?GNU_MAGIC_LEN),
+ version=binary_part(Bin, ?GNU_VERSION, ?GNU_VERSION_LEN),
+ uname=binary_part(Bin, 265, 32),
+ gname=binary_part(Bin, 297, 32),
+ devmajor=binary_part(Bin, 329, 8),
+ devminor=binary_part(Bin, 337, 8),
+ atime=binary_part(Bin, 345, 12),
+ ctime=binary_part(Bin, 357, 12),
+ sparse=to_sparse_array(binary_part(Bin, 386, 24*4+1)),
+ real_size=binary_part(Bin, 483, 12)
+ }.
+
+to_star(#header_v7{}=V7, Bin)
+ when is_binary(Bin), byte_size(Bin) =:= ?BLOCK_SIZE ->
+ #header_star{
+ header_v7=V7,
+ magic=binary_part(Bin, ?USTAR_MAGIC, ?USTAR_MAGIC_LEN),
+ version=binary_part(Bin, ?USTAR_VERSION, ?USTAR_VERSION_LEN),
+ uname=binary_part(Bin, ?USTAR_UNAME, ?USTAR_UNAME_LEN),
+ gname=binary_part(Bin, ?USTAR_GNAME, ?USTAR_GNAME_LEN),
+ devmajor=binary_part(Bin, ?USTAR_DEVMAJ, ?USTAR_DEVMAJ_LEN),
+ devminor=binary_part(Bin, ?USTAR_DEVMIN, ?USTAR_DEVMIN_LEN),
+ prefix=binary_part(Bin, 345, 131),
+ atime=binary_part(Bin, 476, 12),
+ ctime=binary_part(Bin, 488, 12),
+ trailer=binary_part(Bin, ?STAR_TRAILER, ?STAR_TRAILER_LEN)
+ }.
+
+to_ustar(#header_v7{}=V7, Bin)
+ when is_binary(Bin), byte_size(Bin) =:= ?BLOCK_SIZE ->
+ #header_ustar{
+ header_v7=V7,
+ magic=binary_part(Bin, ?USTAR_MAGIC, ?USTAR_MAGIC_LEN),
+ version=binary_part(Bin, ?USTAR_VERSION, ?USTAR_VERSION_LEN),
+ uname=binary_part(Bin, ?USTAR_UNAME, ?USTAR_UNAME_LEN),
+ gname=binary_part(Bin, ?USTAR_GNAME, ?USTAR_GNAME_LEN),
+ devmajor=binary_part(Bin, ?USTAR_DEVMAJ, ?USTAR_DEVMAJ_LEN),
+ devminor=binary_part(Bin, ?USTAR_DEVMIN, ?USTAR_DEVMIN_LEN),
+ prefix=binary_part(Bin, 345, 155)
+ }.
+
+to_sparse_array(Bin) when is_binary(Bin) ->
+ MaxEntries = byte_size(Bin) div 24,
+ IsExtended = 1 =:= binary:at(Bin, 24*MaxEntries),
+ Entries = parse_sparse_entries(Bin, MaxEntries-1, []),
+ #sparse_array{
+ entries=Entries,
+ max_entries=MaxEntries,
+ is_extended=IsExtended
+ }.
+
+parse_sparse_entries(<<>>, _, Acc) ->
+ Acc;
+parse_sparse_entries(_, -1, Acc) ->
+ Acc;
+parse_sparse_entries(Bin, N, Acc) ->
+ case to_sparse_entry(binary_part(Bin, N*24, 24)) of
+ nil ->
+ parse_sparse_entries(Bin, N-1, Acc);
+ Entry = #sparse_entry{} ->
+ parse_sparse_entries(Bin, N-1, [Entry|Acc])
+ end.
+
+-define(EMPTY_ENTRY, <<0,0,0,0,0,0,0,0,0,0,0,0>>).
+to_sparse_entry(Bin) when is_binary(Bin), byte_size(Bin) =:= 24 ->
+ OffsetBin = binary_part(Bin, 0, 12),
+ NumBytesBin = binary_part(Bin, 12, 12),
+ case {OffsetBin, NumBytesBin} of
+ {?EMPTY_ENTRY, ?EMPTY_ENTRY} ->
+ nil;
+ _ ->
+ #sparse_entry{
+ offset=parse_numeric(OffsetBin),
+ num_bytes=parse_numeric(NumBytesBin)}
+ end.
+
+-spec get_format(binary()) -> {ok, pos_integer(), header_v7()}
+ | ?FORMAT_UNKNOWN
+ | {error, term()}.
+get_format(Bin) when is_binary(Bin), byte_size(Bin) =:= ?BLOCK_SIZE ->
+ do_get_format(to_v7(Bin), Bin).
+
+do_get_format({error, _} = Err, _Bin) ->
+ Err;
+do_get_format(#header_v7{}=V7, Bin)
+ when is_binary(Bin), byte_size(Bin) =:= ?BLOCK_SIZE ->
+ Checksum = parse_octal(V7#header_v7.checksum),
+ Chk1 = compute_checksum(Bin),
+ Chk2 = compute_signed_checksum(Bin),
+ if Checksum =/= Chk1 andalso Checksum =/= Chk2 ->
+ ?FORMAT_UNKNOWN;
+ true ->
+ %% guess magic
+ Ustar = to_ustar(V7, Bin),
+ Star = to_star(V7, Bin),
+ Magic = Ustar#header_ustar.magic,
+ Version = Ustar#header_ustar.version,
+ Trailer = Star#header_star.trailer,
+ Format = if
+ Magic =:= ?MAGIC_USTAR, Trailer =:= ?TRAILER_STAR ->
+ ?FORMAT_STAR;
+ Magic =:= ?MAGIC_USTAR ->
+ ?FORMAT_USTAR;
+ Magic =:= ?MAGIC_GNU, Version =:= ?VERSION_GNU ->
+ ?FORMAT_GNU;
+ true ->
+ ?FORMAT_V7
+ end,
+ {ok, Format, V7}
+ end.
+
+unpack_format(Format, #header_v7{}=V7, Bin, Reader)
+ when is_binary(Bin), byte_size(Bin) =:= ?BLOCK_SIZE ->
+ Mtime = parse_numeric(V7#header_v7.mtime),
+ Header0 = #tar_header{
+ name=parse_string(V7#header_v7.name),
+ mode=parse_numeric(V7#header_v7.mode),
+ uid=parse_numeric(V7#header_v7.uid),
+ gid=parse_numeric(V7#header_v7.gid),
+ size=parse_numeric(V7#header_v7.size),
+ mtime=Mtime,
+ atime=Mtime,
+ ctime=Mtime,
+ typeflag=V7#header_v7.typeflag,
+ linkname=parse_string(V7#header_v7.linkname)
+ },
+ Typeflag = Header0#tar_header.typeflag,
+ Header1 = if Format > ?FORMAT_V7 ->
+ unpack_modern(Format, V7, Bin, Header0);
+ true ->
+ Name = Header0#tar_header.name,
+ Header0#tar_header{name=safe_join_path("", Name)}
+ end,
+ HeaderOnly = is_header_only_type(Typeflag),
+ Header2 = if HeaderOnly ->
+ Header1#tar_header{size=0};
+ true ->
+ Header1
+ end,
+ if Typeflag =:= ?TYPE_GNU_SPARSE ->
+ Gnu = to_gnu(V7, Bin),
+ RealSize = parse_numeric(Gnu#header_gnu.real_size),
+ {Sparsemap, Reader2} = parse_sparse_map(Gnu, Reader),
+ Header3 = Header2#tar_header{size=RealSize},
+ {Header3, new_sparse_file_reader(Reader2, Sparsemap, RealSize)};
+ true ->
+ FileReader = #reg_file_reader{
+ handle=Reader,
+ num_bytes=Header2#tar_header.size,
+ size=Header2#tar_header.size,
+ pos = 0
+ },
+ {Header2, FileReader}
+ end.
+
+unpack_modern(Format, #header_v7{}=V7, Bin, #tar_header{}=Header0)
+ when is_binary(Bin) ->
+ Typeflag = Header0#tar_header.typeflag,
+ Ustar = to_ustar(V7, Bin),
+ H0 = Header0#tar_header{
+ uname=parse_string(Ustar#header_ustar.uname),
+ gname=parse_string(Ustar#header_ustar.gname)},
+ H1 = if Typeflag =:= ?TYPE_CHAR
+ orelse Typeflag =:= ?TYPE_BLOCK ->
+ Ma = parse_numeric(Ustar#header_ustar.devmajor),
+ Mi = parse_numeric(Ustar#header_ustar.devminor),
+ H0#tar_header{
+ devmajor=Ma,
+ devminor=Mi
+ };
+ true ->
+ H0
+ end,
+ {Prefix, H2} = case Format of
+ ?FORMAT_USTAR ->
+ {parse_string(Ustar#header_ustar.prefix), H1};
+ ?FORMAT_STAR ->
+ Star = to_star(V7, Bin),
+ Prefix0 = parse_string(Star#header_star.prefix),
+ Atime0 = Star#header_star.atime,
+ Atime = parse_numeric(Atime0),
+ Ctime0 = Star#header_star.ctime,
+ Ctime = parse_numeric(Ctime0),
+ {Prefix0, H1#tar_header{
+ atime=Atime,
+ ctime=Ctime
+ }};
+ _ ->
+ {"", H1}
+ end,
+ Name = H2#tar_header.name,
+ H2#tar_header{name=safe_join_path(Prefix, Name)}.
+
+
+safe_join_path([], Name) ->
+ filename:join([Name]);
+safe_join_path(Prefix, []) ->
+ filename:join([Prefix]);
+safe_join_path(Prefix, Name) ->
+ filename:join(Prefix, Name).
+
+new_sparse_file_reader(Reader, Sparsemap, RealSize) ->
+ true = validate_sparse_entries(Sparsemap, RealSize),
+ #sparse_file_reader{
+ handle = Reader,
+ num_bytes = RealSize,
+ pos = 0,
+ size = RealSize,
+ sparse_map = Sparsemap}.
+
+validate_sparse_entries(Entries, RealSize) ->
+ validate_sparse_entries(Entries, RealSize, 0, 0).
+validate_sparse_entries([], _RealSize, _I, _LastOffset) ->
+ true;
+validate_sparse_entries([#sparse_entry{}=Entry|Rest], RealSize, I, LastOffset) ->
+ Offset = Entry#sparse_entry.offset,
+ NumBytes = Entry#sparse_entry.num_bytes,
+ if
+ Offset > ?MAX_INT64-NumBytes ->
+ throw({error, {invalid_sparse_map_entry, offset_too_large}});
+ Offset+NumBytes > RealSize ->
+ throw({error, {invalid_sparse_map_entry, offset_too_large}});
+ I > 0 andalso LastOffset > Offset ->
+ throw({error, {invalid_sparse_map_entry, overlapping_offsets}});
+ true ->
+ ok
+ end,
+ validate_sparse_entries(Rest, RealSize, I+1, Offset+NumBytes).
+
+
+-spec parse_sparse_map(header_gnu(), reader_type()) ->
+ {[sparse_entry()], reader_type()}.
+parse_sparse_map(#header_gnu{sparse=Sparse}, Reader)
+ when Sparse#sparse_array.is_extended ->
+ parse_sparse_map(Sparse, Reader, []);
+parse_sparse_map(#header_gnu{sparse=Sparse}, Reader) ->
+ {Sparse#sparse_array.entries, Reader}.
+parse_sparse_map(#sparse_array{is_extended=true,entries=Entries}, Reader, Acc) ->
+ case read_block(Reader) of
+ eof ->
+ throw({error, eof});
+ {ok, Block, Reader2} ->
+ Sparse2 = to_sparse_array(Block),
+ parse_sparse_map(Sparse2, Reader2, Entries++Acc)
+ end;
+parse_sparse_map(#sparse_array{entries=Entries}, Reader, Acc) ->
+ Sorted = lists:sort(fun (#sparse_entry{offset=A},#sparse_entry{offset=B}) ->
+ A =< B
+ end, Entries++Acc),
+ {Sorted, Reader}.
+
+%% Defined by taking the sum of the unsigned byte values of the
+%% entire header record, treating the checksum bytes to as ASCII spaces
+compute_checksum(<<H1:?V7_CHKSUM/binary,
+ H2:?V7_CHKSUM_LEN/binary,
+ Rest:(?BLOCK_SIZE - ?V7_CHKSUM - ?V7_CHKSUM_LEN)/binary,
+ _/binary>>) ->
+ C0 = checksum(H1) + (byte_size(H2) * $\s),
+ C1 = checksum(Rest),
+ C0 + C1.
+
+compute_signed_checksum(<<H1:?V7_CHKSUM/binary,
+ H2:?V7_CHKSUM_LEN/binary,
+ Rest:(?BLOCK_SIZE - ?V7_CHKSUM - ?V7_CHKSUM_LEN)/binary,
+ _/binary>>) ->
+ C0 = signed_checksum(H1) + (byte_size(H2) * $\s),
+ C1 = signed_checksum(Rest),
+ C0 + C1.
+
+%% Returns the checksum of a binary.
+checksum(Bin) -> checksum(Bin, 0).
+checksum(<<A/unsigned,Rest/binary>>, Sum) ->
+ checksum(Rest, Sum+A);
+checksum(<<>>, Sum) -> Sum.
+
+signed_checksum(Bin) -> signed_checksum(Bin, 0).
+signed_checksum(<<A/signed,Rest/binary>>, Sum) ->
+ signed_checksum(Rest, Sum+A);
+signed_checksum(<<>>, Sum) -> Sum.
+
+-spec parse_numeric(binary()) -> non_neg_integer().
+parse_numeric(<<>>) ->
+ 0;
+parse_numeric(<<First, _/binary>> = Bin) ->
+ %% check for base-256 format first
+ %% if the bit is set, then all following bits constitute a two's
+ %% complement encoded number in big-endian byte order
+ if
+ First band 16#80 =/= 0 ->
+ %% Handling negative numbers relies on the following identity:
+ %% -a-1 == ^a
+ %% If the number is negative, we use an inversion mask to invert
+ %% the data bytes and treat the value as an unsigned number
+ Inv = if First band 16#40 =/= 0 -> 16#00; true -> 16#FF end,
+ Bytes = binary:bin_to_list(Bin),
+ Reducer = fun (C, {I, X}) ->
+ C1 = C bxor Inv,
+ C2 = if I =:= 0 -> C1 band 16#7F; true -> C1 end,
+ if (X bsr 56) > 0 ->
+ throw({error,integer_overflow});
+ true ->
+ {I+1, (X bsl 8) bor C2}
+ end
+ end,
+ {_, N} = lists:foldl(Reducer, {0,0}, Bytes),
+ if (N bsr 63) > 0 ->
+ throw({error, integer_overflow});
+ true ->
+ if Inv =:= 16#FF ->
+ -1 bxor N;
+ true ->
+ N
+ end
+ end;
+ true ->
+ %% normal case is an octal number
+ parse_octal(Bin)
+ end.
+
+parse_octal(Bin) when is_binary(Bin) ->
+ %% skip leading/trailing zero bytes and spaces
+ do_parse_octal(Bin, <<>>).
+do_parse_octal(<<>>, <<>>) ->
+ 0;
+do_parse_octal(<<>>, Acc) ->
+ case io_lib:fread("~8u", binary:bin_to_list(Acc)) of
+ {error, _} -> throw({error, invalid_tar_checksum});
+ {ok, [Octal], []} -> Octal;
+ {ok, _, _} -> throw({error, invalid_tar_checksum})
+ end;
+do_parse_octal(<<$\s,Rest/binary>>, Acc) ->
+ do_parse_octal(Rest, Acc);
+do_parse_octal(<<0, Rest/binary>>, Acc) ->
+ do_parse_octal(Rest, Acc);
+do_parse_octal(<<C, Rest/binary>>, Acc) ->
+ do_parse_octal(Rest, <<Acc/binary, C>>).
+
+parse_string(Bin) when is_binary(Bin) ->
+ do_parse_string(Bin, <<>>).
+do_parse_string(<<>>, Acc) ->
+ case unicode:characters_to_list(Acc) of
+ Str when is_list(Str) ->
+ Str;
+ {incomplete, _Str, _Rest} ->
+ binary:bin_to_list(Acc);
+ {error, _Str, _Rest} ->
+ throw({error, {bad_header, invalid_string}})
+ end;
+do_parse_string(<<0, _/binary>>, Acc) ->
+ do_parse_string(<<>>, Acc);
+do_parse_string(<<C, Rest/binary>>, Acc) ->
+ do_parse_string(Rest, <<Acc/binary, C>>).
+
+convert_header(Bin, #reader{pos=Pos}=Reader)
+ when byte_size(Bin) =:= ?BLOCK_SIZE, (Pos rem ?BLOCK_SIZE) =:= 0 ->
+ case get_format(Bin) of
+ ?FORMAT_UNKNOWN ->
+ throw({error, bad_header});
+ {ok, Format, V7} ->
+ unpack_format(Format, V7, Bin, Reader);
+ {error, Reason} ->
+ throw({error, {bad_header, Reason}})
+ end;
+convert_header(Bin, #reader{pos=Pos}) when byte_size(Bin) =:= ?BLOCK_SIZE ->
+ throw({error, misaligned_read, Pos});
+convert_header(Bin, _Reader) when byte_size(Bin) =:= 0 ->
+ eof;
+convert_header(_Bin, _Reader) ->
+ throw({error, eof}).
+
+%% Creates a partially-populated header record based
+%% on the provided file_info record. If the file is
+%% a symlink, then `link` is used as the link target.
+%% If the file is a directory, a slash is appended to the name.
+fileinfo_to_header(Name, #file_info{}=Fi, Link) when is_list(Name) ->
+ BaseHeader = #tar_header{name=Name,
+ mtime=0,
+ atime=0,
+ ctime=0,
+ mode=Fi#file_info.mode,
+ typeflag=?TYPE_REGULAR},
+ do_fileinfo_to_header(BaseHeader, Fi, Link).
+
+do_fileinfo_to_header(Header, #file_info{size=Size,type=regular}, _Link) ->
+ Header#tar_header{size=Size,typeflag=?TYPE_REGULAR};
+do_fileinfo_to_header(#tar_header{name=Name}=Header,
+ #file_info{type=directory}, _Link) ->
+ Header#tar_header{name=Name++"/",typeflag=?TYPE_DIR};
+do_fileinfo_to_header(Header, #file_info{type=symlink}, Link) ->
+ Header#tar_header{typeflag=?TYPE_SYMLINK,linkname=Link};
+do_fileinfo_to_header(Header, #file_info{type=device,mode=Mode}=Fi, _Link)
+ when (Mode band ?S_IFMT) =:= ?S_IFCHR ->
+ Header#tar_header{typeflag=?TYPE_CHAR,
+ devmajor=Fi#file_info.major_device,
+ devminor=Fi#file_info.minor_device};
+do_fileinfo_to_header(Header, #file_info{type=device,mode=Mode}=Fi, _Link)
+ when (Mode band ?S_IFMT) =:= ?S_IFBLK ->
+ Header#tar_header{typeflag=?TYPE_BLOCK,
+ devmajor=Fi#file_info.major_device,
+ devminor=Fi#file_info.minor_device};
+do_fileinfo_to_header(Header, #file_info{type=other,mode=Mode}, _Link)
+ when (Mode band ?S_IFMT) =:= ?S_FIFO ->
+ Header#tar_header{typeflag=?TYPE_FIFO};
+do_fileinfo_to_header(Header, Fi, _Link) ->
+ {error, {invalid_file_type, Header#tar_header.name, Fi}}.
+
+is_ascii(Str) when is_list(Str) ->
+ not lists:any(fun (Char) -> Char >= 16#80 end, Str);
+is_ascii(Bin) when is_binary(Bin) ->
+ is_ascii1(Bin).
+
+is_ascii1(<<>>) ->
+ true;
+is_ascii1(<<C,_Rest/binary>>) when C >= 16#80 ->
+ false;
+is_ascii1(<<_, Rest/binary>>) ->
+ is_ascii1(Rest).
+
+to_ascii(Str) when is_list(Str) ->
+ case is_ascii(Str) of
+ true ->
+ unicode:characters_to_binary(Str);
+ false ->
+ Chars = lists:filter(fun (Char) -> Char < 16#80 end, Str),
+ unicode:characters_to_binary(Chars)
+ end;
+to_ascii(Bin) when is_binary(Bin) ->
+ to_ascii(Bin, <<>>).
+to_ascii(<<>>, Acc) ->
+ Acc;
+to_ascii(<<C, Rest/binary>>, Acc) when C < 16#80 ->
+ to_ascii(Rest, <<Acc/binary,C>>);
+to_ascii(<<_, Rest/binary>>, Acc) ->
+ to_ascii(Rest, Acc).
+
+is_header_only_type(?TYPE_SYMLINK) -> true;
+is_header_only_type(?TYPE_LINK) -> true;
+is_header_only_type(?TYPE_DIR) -> true;
+is_header_only_type(_) -> false.
+
+foldl_read(#reader{access=read}=Reader, Fun, Accu, #read_opts{}=Opts)
+ when is_function(Fun,4) ->
+ case foldl_read0(Reader, Fun, Accu, Opts) of
+ {ok, Result, _Reader2} ->
+ Result;
+ {error, _} = Err ->
+ Err
+ end;
+foldl_read(#reader{access=Access}, _Fun, _Accu, _Opts) ->
+ {error, {read_mode_expected, Access}};
+foldl_read(TarName, Fun, Accu, #read_opts{}=Opts)
+ when is_function(Fun,4) ->
+ try open(TarName, [read|Opts#read_opts.open_mode]) of
+ {ok, #reader{access=read}=Reader} ->
+ try
+ foldl_read(Reader, Fun, Accu, Opts)
+ after
+ _ = close(Reader)
+ end;
+ {error, _} = Err ->
+ Err
+ catch
+ throw:Err ->
+ Err
+ end.
+
+foldl_read0(Reader, Fun, Accu, Opts) ->
+ try foldl_read1(Fun, Accu, Reader, Opts, #{}) of
+ {ok,_,_} = Ok ->
+ Ok
+ catch
+ throw:{error, {Reason, Format, Args}} ->
+ read_verbose(Opts, Format, Args),
+ {error, Reason};
+ throw:Err ->
+ Err
+ end.
+
+foldl_read1(Fun, Accu0, Reader0, Opts, ExtraHeaders) ->
+ {ok, Reader1} = skip_unread(Reader0),
+ case get_header(Reader1) of
+ eof ->
+ Fun(eof, Reader1, Opts, Accu0);
+ {Header, Reader2} ->
+ case Header#tar_header.typeflag of
+ ?TYPE_X_HEADER ->
+ {ExtraHeaders2, Reader3} = parse_pax(Reader2),
+ ExtraHeaders3 = maps:merge(ExtraHeaders, ExtraHeaders2),
+ foldl_read1(Fun, Accu0, Reader3, Opts, ExtraHeaders3);
+ ?TYPE_GNU_LONGNAME ->
+ {RealName, Reader3} = get_real_name(Reader2),
+ ExtraHeaders2 = maps:put(?PAX_PATH,
+ parse_string(RealName), ExtraHeaders),
+ foldl_read1(Fun, Accu0, Reader3, Opts, ExtraHeaders2);
+ ?TYPE_GNU_LONGLINK ->
+ {RealName, Reader3} = get_real_name(Reader2),
+ ExtraHeaders2 = maps:put(?PAX_LINKPATH,
+ parse_string(RealName), ExtraHeaders),
+ foldl_read1(Fun, Accu0, Reader3, Opts, ExtraHeaders2);
+ _ ->
+ Header1 = merge_pax(Header, ExtraHeaders),
+ {ok, NewAccu, Reader3} = Fun(Header1, Reader2, Opts, Accu0),
+ foldl_read1(Fun, NewAccu, Reader3, Opts, #{})
+ end
+ end.
+
+%% Applies all known PAX attributes to the current tar header
+-spec merge_pax(tar_header(), #{binary() => binary()}) -> tar_header().
+merge_pax(Header, ExtraHeaders) when is_map(ExtraHeaders) ->
+ do_merge_pax(Header, maps:to_list(ExtraHeaders)).
+
+do_merge_pax(Header, []) ->
+ Header;
+do_merge_pax(Header, [{?PAX_PATH, Path}|Rest]) ->
+ do_merge_pax(Header#tar_header{name=unicode:characters_to_list(Path)}, Rest);
+do_merge_pax(Header, [{?PAX_LINKPATH, LinkPath}|Rest]) ->
+ do_merge_pax(Header#tar_header{linkname=unicode:characters_to_list(LinkPath)}, Rest);
+do_merge_pax(Header, [{?PAX_GNAME, Gname}|Rest]) ->
+ do_merge_pax(Header#tar_header{gname=unicode:characters_to_list(Gname)}, Rest);
+do_merge_pax(Header, [{?PAX_UNAME, Uname}|Rest]) ->
+ do_merge_pax(Header#tar_header{uname=unicode:characters_to_list(Uname)}, Rest);
+do_merge_pax(Header, [{?PAX_UID, Uid}|Rest]) ->
+ Uid2 = binary_to_integer(Uid),
+ do_merge_pax(Header#tar_header{uid=Uid2}, Rest);
+do_merge_pax(Header, [{?PAX_GID, Gid}|Rest]) ->
+ Gid2 = binary_to_integer(Gid),
+ do_merge_pax(Header#tar_header{gid=Gid2}, Rest);
+do_merge_pax(Header, [{?PAX_ATIME, Atime}|Rest]) ->
+ Atime2 = parse_pax_time(Atime),
+ do_merge_pax(Header#tar_header{atime=Atime2}, Rest);
+do_merge_pax(Header, [{?PAX_MTIME, Mtime}|Rest]) ->
+ Mtime2 = parse_pax_time(Mtime),
+ do_merge_pax(Header#tar_header{mtime=Mtime2}, Rest);
+do_merge_pax(Header, [{?PAX_CTIME, Ctime}|Rest]) ->
+ Ctime2 = parse_pax_time(Ctime),
+ do_merge_pax(Header#tar_header{ctime=Ctime2}, Rest);
+do_merge_pax(Header, [{?PAX_SIZE, Size}|Rest]) ->
+ Size2 = binary_to_integer(Size),
+ do_merge_pax(Header#tar_header{size=Size2}, Rest);
+do_merge_pax(Header, [{<<?PAX_XATTR_STR, _Key/binary>>, _Value}|Rest]) ->
+ do_merge_pax(Header, Rest);
+do_merge_pax(Header, [_Ignore|Rest]) ->
+ do_merge_pax(Header, Rest).
+
+%% Returns the time since UNIX epoch as a datetime
+-spec parse_pax_time(binary()) -> tar_time().
+parse_pax_time(Bin) when is_binary(Bin) ->
+ TotalNano = case binary:split(Bin, [<<$.>>]) of
+ [SecondsStr, NanoStr0] ->
+ Seconds = binary_to_integer(SecondsStr),
+ if byte_size(NanoStr0) < ?MAX_NANO_INT_SIZE ->
+ %% right pad
+ PaddingN = ?MAX_NANO_INT_SIZE-byte_size(NanoStr0),
+ Padding = binary:copy(<<$0>>, PaddingN),
+ NanoStr1 = <<NanoStr0/binary,Padding/binary>>,
+ Nano = binary_to_integer(NanoStr1),
+ (Seconds*?BILLION)+Nano;
+ byte_size(NanoStr0) > ?MAX_NANO_INT_SIZE ->
+ %% right truncate
+ NanoStr1 = binary_part(NanoStr0, 0, ?MAX_NANO_INT_SIZE),
+ Nano = binary_to_integer(NanoStr1),
+ (Seconds*?BILLION)+Nano;
+ true ->
+ (Seconds*?BILLION)+binary_to_integer(NanoStr0)
+ end;
+ [SecondsStr] ->
+ binary_to_integer(SecondsStr)*?BILLION
+ end,
+ %% truncate to microseconds
+ Micro = TotalNano div 1000,
+ Mega = Micro div 1000000000000,
+ Secs = Micro div 1000000 - (Mega*1000000),
+ Secs.
+
+%% Given a regular file reader, reads the whole file and
+%% parses all extended attributes it contains.
+parse_pax(#reg_file_reader{handle=Handle,num_bytes=0}) ->
+ {#{}, Handle};
+parse_pax(#reg_file_reader{handle=Handle0,num_bytes=NumBytes}) ->
+ case do_read(Handle0, NumBytes) of
+ {ok, Bytes, Handle1} ->
+ do_parse_pax(Handle1, Bytes, #{});
+ {error, _} = Err ->
+ throw(Err)
+ end.
+
+do_parse_pax(Reader, <<>>, Headers) ->
+ {Headers, Reader};
+do_parse_pax(Reader, Bin, Headers) ->
+ {Key, Value, Residual} = parse_pax_record(Bin),
+ NewHeaders = maps:put(Key, Value, Headers),
+ do_parse_pax(Reader, Residual, NewHeaders).
+
+%% Parse an extended attribute
+parse_pax_record(Bin) when is_binary(Bin) ->
+ case binary:split(Bin, [<<$\n>>]) of
+ [Record, Residual] ->
+ case [X || X <- binary:split(Record, [<<$\s>>], [global]), X =/= <<>>] of
+ [_Len, Record1] ->
+ case [X || X <- binary:split(Record1, [<<$=>>], [global]), X =/= <<>>] of
+ [AttrName, AttrValue] ->
+ {AttrName, AttrValue, Residual};
+ _Other ->
+ throw({error, malformed_pax_record})
+ end;
+ _Other ->
+ throw({error, malformed_pax_record})
+ end;
+ _Other ->
+ throw({error, malformed_pax_record})
+ end.
+
+get_real_name(#reg_file_reader{handle=Handle,num_bytes=0}) ->
+ {"", Handle};
+get_real_name(#reg_file_reader{handle=Handle0,num_bytes=NumBytes}) ->
+ case do_read(Handle0, NumBytes) of
+ {ok, RealName, Handle1} ->
+ {RealName, Handle1};
+ {error, _} = Err ->
+ throw(Err)
+ end;
+get_real_name(#sparse_file_reader{num_bytes=NumBytes}=Reader0) ->
+ case do_read(Reader0, NumBytes) of
+ {ok, RealName, Reader1} ->
+ {RealName, Reader1};
+ {error, _} = Err ->
+ throw(Err)
+ end.
+
+%% Skip the remaining bytes for the current file entry
+skip_file(#reg_file_reader{handle=Handle0,pos=Pos,size=Size}=Reader) ->
+ Padding = skip_padding(Size),
+ AbsPos = Handle0#reader.pos + (Size-Pos) + Padding,
+ case do_position(Handle0, AbsPos) of
+ {ok, _, Handle1} ->
+ Reader#reg_file_reader{handle=Handle1,num_bytes=0,pos=Size};
+ Err ->
+ throw(Err)
+ end;
+skip_file(#sparse_file_reader{pos=Pos,size=Size}=Reader) ->
+ case do_read(Reader, Size-Pos) of
+ {ok, _, Reader2} ->
+ Reader2;
+ Err ->
+ throw(Err)
+ end.
+
+skip_padding(0) ->
+ 0;
+skip_padding(Size) when (Size rem ?BLOCK_SIZE) =:= 0 ->
+ 0;
+skip_padding(Size) when Size =< ?BLOCK_SIZE ->
+ ?BLOCK_SIZE - Size;
+skip_padding(Size) ->
+ ?BLOCK_SIZE - (Size rem ?BLOCK_SIZE).
+
+skip_unread(#reader{pos=Pos}=Reader0) when (Pos rem ?BLOCK_SIZE) > 0 ->
+ Padding = skip_padding(Pos + ?BLOCK_SIZE),
+ AbsPos = Pos + Padding,
+ case do_position(Reader0, AbsPos) of
+ {ok, _, Reader1} ->
+ {ok, Reader1};
+ Err ->
+ throw(Err)
+ end;
+skip_unread(#reader{}=Reader) ->
+ {ok, Reader};
+skip_unread(#reg_file_reader{handle=Handle,num_bytes=0}) ->
+ skip_unread(Handle);
+skip_unread(#reg_file_reader{}=Reader) ->
+ #reg_file_reader{handle=Handle} = skip_file(Reader),
+ {ok, Handle};
+skip_unread(#sparse_file_reader{handle=Handle,num_bytes=0}) ->
+ skip_unread(Handle);
+skip_unread(#sparse_file_reader{}=Reader) ->
+ #sparse_file_reader{handle=Handle} = skip_file(Reader),
+ {ok, Handle}.
+
+write_extracted_element(#tar_header{name=Name,typeflag=Type},
+ Bin,
+ #read_opts{output=memory}=Opts) ->
+ case typeflag(Type) of
+ regular ->
+ read_verbose(Opts, "x ~ts~n", [Name]),
+ {ok, {Name, Bin}};
+ _ ->
+ ok
+ end;
+write_extracted_element(#tar_header{name=Name0}=Header, Bin, Opts) ->
+ Name1 = make_safe_path(Name0, Opts),
+ Created =
+ case typeflag(Header#tar_header.typeflag) of
+ regular ->
+ create_regular(Name1, Name0, Bin, Opts);
+ directory ->
+ read_verbose(Opts, "x ~ts~n", [Name0]),
+ create_extracted_dir(Name1, Opts);
+ symlink ->
+ read_verbose(Opts, "x ~ts~n", [Name0]),
+ create_symlink(Name1, Header#tar_header.linkname, Opts);
+ Device when Device =:= char orelse Device =:= block ->
+ %% char/block devices will be created as empty files
+ %% and then have their major/minor device set later
+ create_regular(Name1, Name0, <<>>, Opts);
+ fifo ->
+ %% fifo devices will be created as empty files
+ create_regular(Name1, Name0, <<>>, Opts);
+ Other -> % Ignore.
+ read_verbose(Opts, "x ~ts - unsupported type ~p~n",
+ [Name0, Other]),
+ not_written
+ end,
+ case Created of
+ ok -> set_extracted_file_info(Name1, Header);
+ not_written -> ok
+ end.
+
+make_safe_path([$/|Path], Opts) ->
+ make_safe_path(Path, Opts);
+make_safe_path(Path, #read_opts{cwd=Cwd}) ->
+ case r3_hex_filename:safe_relative_path(Path) of
+ unsafe ->
+ throw({error,{Path,unsafe_path}});
+ SafePath ->
+ filename:absname(SafePath, Cwd)
+ end.
+
+create_regular(Name, NameInArchive, Bin, Opts) ->
+ case write_extracted_file(Name, Bin, Opts) of
+ not_written ->
+ read_verbose(Opts, "x ~ts - exists, not created~n", [NameInArchive]),
+ not_written;
+ Ok ->
+ read_verbose(Opts, "x ~ts~n", [NameInArchive]),
+ Ok
+ end.
+
+create_extracted_dir(Name, _Opts) ->
+ case file:make_dir(Name) of
+ ok -> ok;
+ {error,enotsup} -> not_written;
+ {error,eexist} -> not_written;
+ {error,enoent} -> make_dirs(Name, dir);
+ {error,Reason} -> throw({error, Reason})
+ end.
+
+create_symlink(Name, Linkname, Opts) ->
+ case file:make_symlink(Linkname, Name) of
+ ok -> ok;
+ {error,enoent} ->
+ ok = make_dirs(Name, file),
+ create_symlink(Name, Linkname, Opts);
+ {error,eexist} -> not_written;
+ {error,enotsup} ->
+ read_verbose(Opts, "x ~ts - symbolic links not supported~n", [Name]),
+ not_written;
+ {error,Reason} -> throw({error, Reason})
+ end.
+
+write_extracted_file(Name, Bin, Opts) ->
+ Write =
+ case Opts#read_opts.keep_old_files of
+ true ->
+ case file:read_file_info(Name) of
+ {ok, _} -> false;
+ _ -> true
+ end;
+ false -> true
+ end,
+ case Write of
+ true -> write_file(Name, Bin);
+ false -> not_written
+ end.
+
+write_file(Name, Bin) ->
+ case file:write_file(Name, Bin) of
+ ok -> ok;
+ {error,enoent} ->
+ case make_dirs(Name, file) of
+ ok ->
+ write_file(Name, Bin);
+ {error,Reason} ->
+ throw({error, Reason})
+ end;
+ {error,Reason} ->
+ throw({error, Reason})
+ end.
+
+set_extracted_file_info(_, #tar_header{typeflag = ?TYPE_SYMLINK}) -> ok;
+set_extracted_file_info(_, #tar_header{typeflag = ?TYPE_LINK}) -> ok;
+set_extracted_file_info(Name, #tar_header{typeflag = ?TYPE_CHAR}=Header) ->
+ set_device_info(Name, Header);
+set_extracted_file_info(Name, #tar_header{typeflag = ?TYPE_BLOCK}=Header) ->
+ set_device_info(Name, Header);
+set_extracted_file_info(Name, #tar_header{mtime=Mtime,mode=Mode}) ->
+ Info = #file_info{mode=Mode, mtime=Mtime},
+ file:write_file_info(Name, Info, [{time, posix}]).
+
+set_device_info(Name, #tar_header{}=Header) ->
+ Mtime = Header#tar_header.mtime,
+ Mode = Header#tar_header.mode,
+ Devmajor = Header#tar_header.devmajor,
+ Devminor = Header#tar_header.devminor,
+ Info = #file_info{
+ mode=Mode,
+ mtime=Mtime,
+ major_device=Devmajor,
+ minor_device=Devminor
+ },
+ file:write_file_info(Name, Info).
+
+%% Makes all directories leading up to the file.
+
+make_dirs(Name, file) ->
+ filelib:ensure_dir(Name);
+make_dirs(Name, dir) ->
+ filelib:ensure_dir(filename:join(Name,"*")).
+
+%% Prints the message on if the verbose option is given (for reading).
+read_verbose(#read_opts{verbose=true}, Format, Args) ->
+ io:format(Format, Args);
+read_verbose(_, _, _) ->
+ ok.
+
+%% Prints the message on if the verbose option is given.
+add_verbose(#add_opts{verbose=true}, Format, Args) ->
+ io:format(Format, Args);
+add_verbose(_, _, _) ->
+ ok.
+
+%%%%%%%%%%%%%%%%%%
+%% I/O primitives
+%%%%%%%%%%%%%%%%%%
+
+do_write(#reader{handle=Handle,func=Fun}=Reader0, Data)
+ when is_function(Fun,2) ->
+ case Fun(write,{Handle,Data}) of
+ ok ->
+ {ok, Pos, Reader1} = do_position(Reader0, {cur,0}),
+ {ok, Reader1#reader{pos=Pos}};
+ {error, _} = Err ->
+ Err
+ end.
+
+do_copy(#reader{func=Fun}=Reader, Source, #add_opts{chunk_size=0}=Opts)
+ when is_function(Fun, 2) ->
+ do_copy(Reader, Source, Opts#add_opts{chunk_size=65536});
+do_copy(#reader{func=Fun}=Reader, Source, #add_opts{chunk_size=ChunkSize})
+ when is_function(Fun, 2) ->
+ case file:open(Source, [read, binary]) of
+ {ok, SourceFd} ->
+ case copy_chunked(Reader, SourceFd, ChunkSize, 0) of
+ {ok, _Copied, _Reader2} = Ok->
+ _ = file:close(SourceFd),
+ Ok;
+ Err ->
+ _ = file:close(SourceFd),
+ throw(Err)
+ end;
+ Err ->
+ throw(Err)
+ end.
+
+copy_chunked(#reader{}=Reader, Source, ChunkSize, Copied) ->
+ case file:read(Source, ChunkSize) of
+ {ok, Bin} ->
+ {ok, Reader2} = do_write(Reader, Bin),
+ copy_chunked(Reader2, Source, ChunkSize, Copied+byte_size(Bin));
+ eof ->
+ {ok, Copied, Reader};
+ Other ->
+ Other
+ end.
+
+
+do_position(#reader{handle=Handle,func=Fun}=Reader, Pos)
+ when is_function(Fun,2)->
+ case Fun(position, {Handle,Pos}) of
+ {ok, NewPos} ->
+ %% since Pos may not always be an absolute seek,
+ %% make sure we update the reader with the new absolute position
+ {ok, AbsPos} = Fun(position, {Handle, {cur, 0}}),
+ {ok, NewPos, Reader#reader{pos=AbsPos}};
+ Other ->
+ Other
+ end.
+
+do_read(#reg_file_reader{handle=Handle,pos=Pos,size=Size}=Reader, Len) ->
+ NumBytes = Size - Pos,
+ ActualLen = if NumBytes - Len < 0 -> NumBytes; true -> Len end,
+ case do_read(Handle, ActualLen) of
+ {ok, Bin, Handle2} ->
+ NewPos = Pos + ActualLen,
+ NumBytes2 = Size - NewPos,
+ Reader1 = Reader#reg_file_reader{
+ handle=Handle2,
+ pos=NewPos,
+ num_bytes=NumBytes2},
+ {ok, Bin, Reader1};
+ Other ->
+ Other
+ end;
+do_read(#sparse_file_reader{}=Reader, Len) ->
+ do_sparse_read(Reader, Len);
+do_read(#reader{pos=Pos,handle=Handle,func=Fun}=Reader, Len)
+ when is_function(Fun,2)->
+ %% Always convert to binary internally
+ case Fun(read2,{Handle,Len}) of
+ {ok, List} when is_list(List) ->
+ Bin = list_to_binary(List),
+ NewPos = Pos+byte_size(Bin),
+ {ok, Bin, Reader#reader{pos=NewPos}};
+ {ok, Bin} when is_binary(Bin) ->
+ NewPos = Pos+byte_size(Bin),
+ {ok, Bin, Reader#reader{pos=NewPos}};
+ Other ->
+ Other
+ end.
+
+
+do_sparse_read(Reader, Len) ->
+ do_sparse_read(Reader, Len, <<>>).
+
+do_sparse_read(#sparse_file_reader{sparse_map=[#sparse_entry{num_bytes=0}|Entries]
+ }=Reader0, Len, Acc) ->
+ %% skip all empty fragments
+ Reader1 = Reader0#sparse_file_reader{sparse_map=Entries},
+ do_sparse_read(Reader1, Len, Acc);
+do_sparse_read(#sparse_file_reader{sparse_map=[],
+ pos=Pos,size=Size}=Reader0, Len, Acc)
+ when Pos < Size ->
+ %% if there are no more fragments, it is possible that there is one last sparse hole
+ %% this behaviour matches the BSD tar utility
+ %% however, GNU tar stops returning data even if we haven't reached the end
+ {ok, Bin, Reader1} = read_sparse_hole(Reader0, Size, Len),
+ do_sparse_read(Reader1, Len-byte_size(Bin), <<Acc/binary,Bin/binary>>);
+do_sparse_read(#sparse_file_reader{sparse_map=[]}=Reader, _Len, Acc) ->
+ {ok, Acc, Reader};
+do_sparse_read(#sparse_file_reader{}=Reader, 0, Acc) ->
+ {ok, Acc, Reader};
+do_sparse_read(#sparse_file_reader{sparse_map=[#sparse_entry{offset=Offset}|_],
+ pos=Pos}=Reader0, Len, Acc)
+ when Pos < Offset ->
+ {ok, Bin, Reader1} = read_sparse_hole(Reader0, Offset, Offset-Pos),
+ do_sparse_read(Reader1, Len-byte_size(Bin), <<Acc/binary,Bin/binary>>);
+do_sparse_read(#sparse_file_reader{sparse_map=[Entry|Entries],
+ pos=Pos}=Reader0, Len, Acc) ->
+ %% we're in a data fragment, so read from it
+ %% end offset of fragment
+ EndPos = Entry#sparse_entry.offset + Entry#sparse_entry.num_bytes,
+ %% bytes left in fragment
+ NumBytes = EndPos - Pos,
+ ActualLen = if Len > NumBytes -> NumBytes; true -> Len end,
+ case do_read(Reader0#sparse_file_reader.handle, ActualLen) of
+ {ok, Bin, Handle} ->
+ BytesRead = byte_size(Bin),
+ ActualEndPos = Pos+BytesRead,
+ Reader1 = if ActualEndPos =:= EndPos ->
+ Reader0#sparse_file_reader{sparse_map=Entries};
+ true ->
+ Reader0
+ end,
+ Size = Reader1#sparse_file_reader.size,
+ NumBytes2 = Size - ActualEndPos,
+ Reader2 = Reader1#sparse_file_reader{
+ handle=Handle,
+ pos=ActualEndPos,
+ num_bytes=NumBytes2},
+ do_sparse_read(Reader2, Len-byte_size(Bin), <<Acc/binary,Bin/binary>>);
+ Other ->
+ Other
+ end.
+
+%% Reads a sparse hole ending at Offset
+read_sparse_hole(#sparse_file_reader{pos=Pos}=Reader, Offset, Len) ->
+ N = Offset - Pos,
+ N2 = if N > Len ->
+ Len;
+ true ->
+ N
+ end,
+ Bin = <<0:N2/unit:8>>,
+ NumBytes = Reader#sparse_file_reader.size - (Pos+N2),
+ {ok, Bin, Reader#sparse_file_reader{
+ num_bytes=NumBytes,
+ pos=Pos+N2}}.
+
+-spec do_close(reader()) -> ok | {error, term()}.
+do_close(#reader{handle=Handle,func=Fun}) when is_function(Fun,2) ->
+ Fun(close,Handle).
+
+%%%%%%%%%%%%%%%%%%
+%% Option parsing
+%%%%%%%%%%%%%%%%%%
+
+extract_opts(List) ->
+ extract_opts(List, default_options()).
+
+table_opts(List) ->
+ read_opts(List, default_options()).
+
+default_options() ->
+ {ok, Cwd} = file:get_cwd(),
+ #read_opts{cwd=Cwd}.
+
+extract_opts([keep_old_files|Rest], Opts) ->
+ extract_opts(Rest, Opts#read_opts{keep_old_files=true});
+extract_opts([{cwd, Cwd}|Rest], Opts) ->
+ extract_opts(Rest, Opts#read_opts{cwd=Cwd});
+extract_opts([{files, Files}|Rest], Opts) ->
+ Set = ordsets:from_list(Files),
+ extract_opts(Rest, Opts#read_opts{files=Set});
+extract_opts([memory|Rest], Opts) ->
+ extract_opts(Rest, Opts#read_opts{output=memory});
+extract_opts([compressed|Rest], Opts=#read_opts{open_mode=OpenMode}) ->
+ extract_opts(Rest, Opts#read_opts{open_mode=[compressed|OpenMode]});
+extract_opts([cooked|Rest], Opts=#read_opts{open_mode=OpenMode}) ->
+ extract_opts(Rest, Opts#read_opts{open_mode=[cooked|OpenMode]});
+extract_opts([verbose|Rest], Opts) ->
+ extract_opts(Rest, Opts#read_opts{verbose=true});
+extract_opts([Other|Rest], Opts) ->
+ extract_opts(Rest, read_opts([Other], Opts));
+extract_opts([], Opts) ->
+ Opts.
+
+read_opts([compressed|Rest], Opts=#read_opts{open_mode=OpenMode}) ->
+ read_opts(Rest, Opts#read_opts{open_mode=[compressed|OpenMode]});
+read_opts([cooked|Rest], Opts=#read_opts{open_mode=OpenMode}) ->
+ read_opts(Rest, Opts#read_opts{open_mode=[cooked|OpenMode]});
+read_opts([verbose|Rest], Opts) ->
+ read_opts(Rest, Opts#read_opts{verbose=true});
+read_opts([_|Rest], Opts) ->
+ read_opts(Rest, Opts);
+read_opts([], Opts) ->
+ Opts.
diff --git a/src/r3_hex_erl_tar.hrl b/src/r3_hex_erl_tar.hrl
new file mode 100644
index 0000000..4834b2d
--- /dev/null
+++ b/src/r3_hex_erl_tar.hrl
@@ -0,0 +1,405 @@
+%% Vendored from hex_core v0.5.0, do not edit manually
+
+% Copied from https://github.com/erlang/otp/blob/OTP-20.0.1/lib/stdlib/src/erl_tar.hrl
+
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+
+%% Options used when adding files to a tar archive.
+-record(add_opts, {
+ read_info, %% Fun to use for read file/link info.
+ chunk_size = 0, %% For file reading when sending to sftp. 0=do not chunk
+ verbose = false, %% Verbose on/off.
+ atime = undefined,
+ mtime = undefined,
+ ctime = undefined,
+ uid = 0,
+ gid = 0}).
+-type add_opts() :: #add_opts{}.
+
+%% Options used when reading a tar archive.
+-record(read_opts, {
+ cwd :: string(), %% Current working directory.
+ keep_old_files = false :: boolean(), %% Owerwrite or not.
+ files = all, %% Set of files to extract (or all)
+ output = file :: 'file' | 'memory',
+ open_mode = [], %% Open mode options.
+ verbose = false :: boolean()}). %% Verbose on/off.
+-type read_opts() :: #read_opts{}.
+
+-type add_opt() :: dereference |
+ verbose |
+ {chunks, pos_integer()}.
+
+-type extract_opt() :: {cwd, string()} |
+ {files, [string()]} |
+ compressed |
+ cooked |
+ memory |
+ keep_old_files |
+ verbose.
+
+-type create_opt() :: compressed |
+ cooked |
+ dereference |
+ verbose.
+
+-type filelist() :: [file:filename() |
+ {string(), binary()} |
+ {string(), file:filename()}].
+
+-type tar_time() :: non_neg_integer().
+
+%% The tar header, once fully parsed.
+-record(tar_header, {
+ name = "" :: string(), %% name of header file entry
+ mode = 8#100644 :: non_neg_integer(), %% permission and mode bits
+ uid = 0 :: non_neg_integer(), %% user id of owner
+ gid = 0 :: non_neg_integer(), %% group id of owner
+ size = 0 :: non_neg_integer(), %% length in bytes
+ mtime :: tar_time(), %% modified time
+ typeflag :: char(), %% type of header entry
+ linkname = "" :: string(), %% target name of link
+ uname = "" :: string(), %% user name of owner
+ gname = "" :: string(), %% group name of owner
+ devmajor = 0 :: non_neg_integer(), %% major number of character or block device
+ devminor = 0 :: non_neg_integer(), %% minor number of character or block device
+ atime :: tar_time(), %% access time
+ ctime :: tar_time() %% status change time
+ }).
+-type tar_header() :: #tar_header{}.
+
+%% Metadata for a sparse file fragment
+-record(sparse_entry, {
+ offset = 0 :: non_neg_integer(),
+ num_bytes = 0 :: non_neg_integer()}).
+-type sparse_entry() :: #sparse_entry{}.
+%% Contains metadata about fragments of a sparse file
+-record(sparse_array, {
+ entries = [] :: [sparse_entry()],
+ is_extended = false :: boolean(),
+ max_entries = 0 :: non_neg_integer()}).
+-type sparse_array() :: #sparse_array{}.
+%% A subset of tar header fields common to all tar implementations
+-record(header_v7, {
+ name :: binary(),
+ mode :: binary(), %% octal
+ uid :: binary(), %% integer
+ gid :: binary(), %% integer
+ size :: binary(), %% integer
+ mtime :: binary(), %% integer
+ checksum :: binary(), %% integer
+ typeflag :: byte(), %% char
+ linkname :: binary()}).
+-type header_v7() :: #header_v7{}.
+%% The set of fields specific to GNU tar formatted archives
+-record(header_gnu, {
+ header_v7 :: header_v7(),
+ magic :: binary(),
+ version :: binary(),
+ uname :: binary(),
+ gname :: binary(),
+ devmajor :: binary(), %% integer
+ devminor :: binary(), %% integer
+ atime :: binary(), %% integer
+ ctime :: binary(), %% integer
+ sparse :: sparse_array(),
+ real_size :: binary()}). %% integer
+-type header_gnu() :: #header_gnu{}.
+%% The set of fields specific to STAR-formatted archives
+-record(header_star, {
+ header_v7 :: header_v7(),
+ magic :: binary(),
+ version :: binary(),
+ uname :: binary(),
+ gname :: binary(),
+ devmajor :: binary(), %% integer
+ devminor :: binary(), %% integer
+ prefix :: binary(),
+ atime :: binary(), %% integer
+ ctime :: binary(), %% integer
+ trailer :: binary()}).
+-type header_star() :: #header_star{}.
+%% The set of fields specific to USTAR-formatted archives
+-record(header_ustar, {
+ header_v7 :: header_v7(),
+ magic :: binary(),
+ version :: binary(),
+ uname :: binary(),
+ gname :: binary(),
+ devmajor :: binary(), %% integer
+ devminor :: binary(), %% integer
+ prefix :: binary()}).
+-type header_ustar() :: #header_ustar{}.
+
+-type header_fields() :: header_v7() |
+ header_gnu() |
+ header_star() |
+ header_ustar().
+
+%% The overall tar reader, it holds the low-level file handle,
+%% its access, position, and the I/O primitives wrapper.
+-record(reader, {
+ handle :: file:io_device() | term(),
+ access :: read | write | ram,
+ pos = 0 :: non_neg_integer(),
+ func :: file_op()
+ }).
+-type reader() :: #reader{}.
+%% A reader for a regular file within the tar archive,
+%% It tracks its current state relative to that file.
+-record(reg_file_reader, {
+ handle :: reader(),
+ num_bytes = 0,
+ pos = 0,
+ size = 0
+ }).
+-type reg_file_reader() :: #reg_file_reader{}.
+%% A reader for a sparse file within the tar archive,
+%% It tracks its current state relative to that file.
+-record(sparse_file_reader, {
+ handle :: reader(),
+ num_bytes = 0, %% bytes remaining
+ pos = 0, %% pos
+ size = 0, %% total size of file
+ sparse_map = #sparse_array{}
+ }).
+-type sparse_file_reader() :: #sparse_file_reader{}.
+
+%% Types for the readers
+-type reader_type() :: reader() | reg_file_reader() | sparse_file_reader().
+-type handle() :: file:io_device() | term().
+
+%% Type for the I/O primitive wrapper function
+-type file_op() :: fun((write | close | read2 | position,
+ {handle(), iodata()} | handle() | {handle(), non_neg_integer()}
+ | {handle(), non_neg_integer()}) ->
+ ok | eof | {ok, string() | binary()} | {ok, non_neg_integer()}
+ | {error, term()}).
+
+%% These constants (except S_IFMT) are
+%% used to determine what type of device
+%% a file is. Namely, `S_IFMT band file_info.mode`
+%% will equal one of these contants, and tells us
+%% which type it is. The stdlib file_info record
+%% does not differentiate between device types, and
+%% will not allow us to differentiate between sockets
+%% and named pipes. These constants are pulled from libc.
+-define(S_IFMT, 61440).
+-define(S_IFSOCK, 49152). %% socket
+-define(S_FIFO, 4096). %% fifo/named pipe
+-define(S_IFBLK, 24576). %% block device
+-define(S_IFCHR, 8192). %% character device
+
+%% Typeflag constants for the tar header
+-define(TYPE_REGULAR, $0). %% regular file
+-define(TYPE_REGULAR_A, 0). %% regular file
+-define(TYPE_LINK, $1). %% hard link
+-define(TYPE_SYMLINK, $2). %% symbolic link
+-define(TYPE_CHAR, $3). %% character device node
+-define(TYPE_BLOCK, $4). %% block device node
+-define(TYPE_DIR, $5). %% directory
+-define(TYPE_FIFO, $6). %% fifo node
+-define(TYPE_CONT, $7). %% reserved
+-define(TYPE_X_HEADER, $x). %% extended header
+-define(TYPE_X_GLOBAL_HEADER, $g). %% global extended header
+-define(TYPE_GNU_LONGNAME, $L). %% next file has a long name
+-define(TYPE_GNU_LONGLINK, $K). %% next file symlinks to a file with a long name
+-define(TYPE_GNU_SPARSE, $S). %% sparse file
+
+%% Mode constants from tar spec
+-define(MODE_ISUID, 4000). %% set uid
+-define(MODE_ISGID, 2000). %% set gid
+-define(MODE_ISVTX, 1000). %% save text (sticky bit)
+-define(MODE_ISDIR, 40000). %% directory
+-define(MODE_ISFIFO, 10000). %% fifo
+-define(MODE_ISREG, 100000). %% regular file
+-define(MODE_ISLNK, 120000). %% symbolic link
+-define(MODE_ISBLK, 60000). %% block special file
+-define(MODE_ISCHR, 20000). %% character special file
+-define(MODE_ISSOCK, 140000). %% socket
+
+%% Keywords for PAX extended header
+-define(PAX_ATIME, <<"atime">>).
+-define(PAX_CHARSET, <<"charset">>).
+-define(PAX_COMMENT, <<"comment">>).
+-define(PAX_CTIME, <<"ctime">>). %% ctime is not a valid pax header
+-define(PAX_GID, <<"gid">>).
+-define(PAX_GNAME, <<"gname">>).
+-define(PAX_LINKPATH, <<"linkpath">>).
+-define(PAX_MTIME, <<"mtime">>).
+-define(PAX_PATH, <<"path">>).
+-define(PAX_SIZE, <<"size">>).
+-define(PAX_UID, <<"uid">>).
+-define(PAX_UNAME, <<"uname">>).
+-define(PAX_XATTR, <<"SCHILY.xattr.">>).
+-define(PAX_XATTR_STR, "SCHILY.xattr.").
+-define(PAX_NONE, <<"">>).
+
+%% Tar format constants
+%% Unknown format
+-define(FORMAT_UNKNOWN, 0).
+%% The format of the original Unix V7 tar tool prior to standardization
+-define(FORMAT_V7, 1).
+%% The old and new GNU formats, incompatible with USTAR.
+%% This covers the old GNU sparse extension, but it does
+%% not cover the GNU sparse extensions using PAX headers,
+%% versions 0.0, 0.1, and 1.0; these fall under the PAX format.
+-define(FORMAT_GNU, 2).
+%% Schily's tar format, which is incompatible with USTAR.
+%% This does not cover STAR extensions to the PAX format; these
+%% fall under the PAX format.
+-define(FORMAT_STAR, 3).
+%% USTAR is the former standardization of tar defined in POSIX.1-1988,
+%% it is incompatible with the GNU and STAR formats.
+-define(FORMAT_USTAR, 4).
+%% PAX is the latest standardization of tar defined in POSIX.1-2001.
+%% This is an extension of USTAR and is "backwards compatible" with it.
+%%
+%% Some newer formats add their own extensions to PAX, such as GNU sparse
+%% files and SCHILY extended attributes. Since they are backwards compatible
+%% with PAX, they will be labelled as "PAX".
+-define(FORMAT_PAX, 5).
+
+%% Magic constants
+-define(MAGIC_GNU, <<"ustar ">>).
+-define(VERSION_GNU, <<" \x00">>).
+-define(MAGIC_USTAR, <<"ustar\x00">>).
+-define(VERSION_USTAR, <<"00">>).
+-define(TRAILER_STAR, <<"tar\x00">>).
+
+%% Size constants
+-define(BLOCK_SIZE, 512). %% size of each block in a tar stream
+-define(NAME_SIZE, 100). %% max length of the name field in USTAR format
+-define(PREFIX_SIZE, 155). %% max length of the prefix field in USTAR format
+
+%% Maximum size of a nanosecond value as an integer
+-define(MAX_NANO_INT_SIZE, 9).
+%% Maximum size of a 64-bit signed integer
+-define(MAX_INT64, (1 bsl 63 - 1)).
+
+-define(PAX_GNU_SPARSE_NUMBLOCKS, <<"GNU.sparse.numblocks">>).
+-define(PAX_GNU_SPARSE_OFFSET, <<"GNU.sparse.offset">>).
+-define(PAX_GNU_SPARSE_NUMBYTES, <<"GNU.sparse.numbytes">>).
+-define(PAX_GNU_SPARSE_MAP, <<"GNU.sparse.map">>).
+-define(PAX_GNU_SPARSE_NAME, <<"GNU.sparse.name">>).
+-define(PAX_GNU_SPARSE_MAJOR, <<"GNU.sparse.major">>).
+-define(PAX_GNU_SPARSE_MINOR, <<"GNU.sparse.minor">>).
+-define(PAX_GNU_SPARSE_SIZE, <<"GNU.sparse.size">>).
+-define(PAX_GNU_SPARSE_REALSIZE, <<"GNU.sparse.realsize">>).
+
+-define(V7_NAME, 0).
+-define(V7_NAME_LEN, 100).
+-define(V7_MODE, 100).
+-define(V7_MODE_LEN, 8).
+-define(V7_UID, 108).
+-define(V7_UID_LEN, 8).
+-define(V7_GID, 116).
+-define(V7_GID_LEN, 8).
+-define(V7_SIZE, 124).
+-define(V7_SIZE_LEN, 12).
+-define(V7_MTIME, 136).
+-define(V7_MTIME_LEN, 12).
+-define(V7_CHKSUM, 148).
+-define(V7_CHKSUM_LEN, 8).
+-define(V7_TYPE, 156).
+-define(V7_TYPE_LEN, 1).
+-define(V7_LINKNAME, 157).
+-define(V7_LINKNAME_LEN, 100).
+
+-define(STAR_TRAILER, 508).
+-define(STAR_TRAILER_LEN, 4).
+
+-define(USTAR_MAGIC, 257).
+-define(USTAR_MAGIC_LEN, 6).
+-define(USTAR_VERSION, 263).
+-define(USTAR_VERSION_LEN, 2).
+-define(USTAR_UNAME, 265).
+-define(USTAR_UNAME_LEN, 32).
+-define(USTAR_GNAME, 297).
+-define(USTAR_GNAME_LEN, 32).
+-define(USTAR_DEVMAJ, 329).
+-define(USTAR_DEVMAJ_LEN, 8).
+-define(USTAR_DEVMIN, 337).
+-define(USTAR_DEVMIN_LEN, 8).
+-define(USTAR_PREFIX, 345).
+-define(USTAR_PREFIX_LEN, 155).
+
+-define(GNU_MAGIC, 257).
+-define(GNU_MAGIC_LEN, 6).
+-define(GNU_VERSION, 263).
+-define(GNU_VERSION_LEN, 2).
+
+%% ?BLOCK_SIZE of zero-bytes.
+%% Two of these in a row mark the end of an archive.
+-define(ZERO_BLOCK, <<0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0>>).
+
+-define(BILLION, 1000000000).
+
+-define(EPOCH, {{1970,1,1}, {0,0,0}}).
diff --git a/src/r3_hex_filename.erl b/src/r3_hex_filename.erl
new file mode 100644
index 0000000..f04c72f
--- /dev/null
+++ b/src/r3_hex_filename.erl
@@ -0,0 +1,60 @@
+%% Vendored from hex_core v0.5.0, do not edit manually
+
+% @private
+% Excerpt from https://github.com/erlang/otp/blob/OTP-20.0.1/lib/stdlib/src/filename.erl#L761-L788
+% with modifications for changing local function calls to remote function calls
+% to the `filename` module, for the functions `pathtype/1`, `split/1`, and `join/1`
+%
+% safe_relative_path/1 was not present in earlier OTP releases.
+
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2017. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(r3_hex_filename).
+-export([safe_relative_path/1]).
+
+safe_relative_path(Path) ->
+ case filename:pathtype(Path) of
+ relative ->
+ Cs0 = filename:split(Path),
+ safe_relative_path_1(Cs0, []);
+ _ ->
+ unsafe
+ end.
+
+safe_relative_path_1(["."|T], Acc) ->
+ safe_relative_path_1(T, Acc);
+safe_relative_path_1([<<".">>|T], Acc) ->
+ safe_relative_path_1(T, Acc);
+safe_relative_path_1([".."|T], Acc) ->
+ climb(T, Acc);
+safe_relative_path_1([<<"..">>|T], Acc) ->
+ climb(T, Acc);
+safe_relative_path_1([H|T], Acc) ->
+ safe_relative_path_1(T, [H|Acc]);
+safe_relative_path_1([], []) ->
+ [];
+safe_relative_path_1([], Acc) ->
+ filename:join(lists:reverse(Acc)).
+
+climb(_, []) ->
+ unsafe;
+climb(T, [_|Acc]) ->
+ safe_relative_path_1(T, Acc).
diff --git a/src/r3_hex_http.erl b/src/r3_hex_http.erl
new file mode 100644
index 0000000..b4794b5
--- /dev/null
+++ b/src/r3_hex_http.erl
@@ -0,0 +1,43 @@
+%% Vendored from hex_core v0.5.0, do not edit manually
+
+-module(r3_hex_http).
+-export([request/5]).
+-ifdef(TEST).
+-export([user_agent/1]).
+-endif.
+-include_lib("r3_hex_core.hrl").
+
+-type method() :: get | post | put | patch | delete.
+-type status() :: non_neg_integer().
+-type headers() :: #{binary() => binary()}.
+-type body() :: {ContentType :: binary(), Body :: binary()} | undefined.
+-type adapter_config() :: map().
+
+-callback request(method(), URI :: binary(), headers(), body(), adapter_config()) ->
+ {ok, status(), headers(), binary()} |
+ {error, term()}.
+
+-spec request(r3_hex_core:config(), method(), URI :: binary(), headers(), body()) ->
+ {ok, {status(), headers(), binary()}} | {error, term()}.
+request(Config, Method, URI, Headers, Body) when is_binary(URI) and is_map(Headers) ->
+ Adapter = maps:get(http_adapter, Config),
+ UserAgentFragment = maps:get(http_user_agent_fragment, Config),
+ Headers2 = put_new(<<"user-agent">>, user_agent(UserAgentFragment), Headers),
+ AdapterConfig = maps:get(http_adapter_config, Config, #{}),
+ Adapter:request(Method, URI, Headers2, Body, AdapterConfig).
+
+user_agent(UserAgentFragment) ->
+ OTPRelease = erlang:system_info(otp_release),
+ ERTSVersion = erlang:system_info(version),
+ OTPString = " (OTP/" ++ OTPRelease ++ ") (erts/" ++ ERTSVersion ++ ")",
+ iolist_to_binary(["hex_core/", ?HEX_CORE_VERSION, " ", UserAgentFragment, OTPString]).
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+
+put_new(Key, Value, Map) ->
+ case maps:find(Key, Map) of
+ {ok, _} -> Map;
+ error -> maps:put(Key, Value, Map)
+ end.
diff --git a/src/r3_hex_http_httpc.erl b/src/r3_hex_http_httpc.erl
new file mode 100644
index 0000000..0be9d16
--- /dev/null
+++ b/src/r3_hex_http_httpc.erl
@@ -0,0 +1,39 @@
+%% Vendored from hex_core v0.5.0, do not edit manually
+
+%% @hidden
+
+-module(r3_hex_http_httpc).
+-behaviour(r3_hex_http).
+-export([request/5]).
+
+%%====================================================================
+%% API functions
+%%====================================================================
+
+request(Method, URI, ReqHeaders, Body, AdapterConfig) ->
+ Profile = maps:get(profile, AdapterConfig, default),
+ Request = build_request(URI, ReqHeaders, Body),
+ {ok, {{_, StatusCode, _}, RespHeaders, RespBody}} =
+ httpc:request(Method, Request, [], [{body_format, binary}], Profile),
+ RespHeaders2 = load_headers(RespHeaders),
+ {ok, {StatusCode, RespHeaders2, RespBody}}.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+
+build_request(URI, ReqHeaders, Body) ->
+ build_request2(binary_to_list(URI), dump_headers(ReqHeaders), Body).
+
+build_request2(URI, ReqHeaders, undefined) ->
+ {URI, ReqHeaders};
+build_request2(URI, ReqHeaders, {ContentType, Body}) ->
+ {URI, ReqHeaders, ContentType, Body}.
+
+dump_headers(Map) ->
+ maps:fold(fun(K, V, Acc) ->
+ [{binary_to_list(K), binary_to_list(V)} | Acc] end, [], Map).
+
+load_headers(List) ->
+ lists:foldl(fun({K, V}, Acc) ->
+ maps:put(list_to_binary(K), list_to_binary(V), Acc) end, #{}, List).
diff --git a/src/r3_hex_pb_names.erl b/src/r3_hex_pb_names.erl
new file mode 100644
index 0000000..455092c
--- /dev/null
+++ b/src/r3_hex_pb_names.erl
@@ -0,0 +1,735 @@
+%% Vendored from hex_core v0.5.0, do not edit manually
+
+%% -*- coding: utf-8 -*-
+%% Automatically generated, do not edit
+%% Generated by gpb_compile version 4.3.1
+-module(r3_hex_pb_names).
+
+-export([encode_msg/2, encode_msg/3]).
+-export([decode_msg/2, decode_msg/3]).
+-export([merge_msgs/3, merge_msgs/4]).
+-export([verify_msg/2, verify_msg/3]).
+-export([get_msg_defs/0]).
+-export([get_msg_names/0]).
+-export([get_group_names/0]).
+-export([get_msg_or_group_names/0]).
+-export([get_enum_names/0]).
+-export([find_msg_def/1, fetch_msg_def/1]).
+-export([find_enum_def/1, fetch_enum_def/1]).
+-export([enum_symbol_by_value/2, enum_value_by_symbol/2]).
+-export([get_service_names/0]).
+-export([get_service_def/1]).
+-export([get_rpc_names/1]).
+-export([find_rpc_def/2, fetch_rpc_def/2]).
+-export([get_package_name/0]).
+-export([gpb_version_as_string/0, gpb_version_as_list/0]).
+
+
+%% enumerated types
+
+-export_type([]).
+
+%% message types
+-type 'Names'() ::
+ #{packages => ['Package'()], % = 1
+ repository => iodata() % = 2
+ }.
+
+-type 'Package'() ::
+ #{name => iodata() % = 1
+ }.
+
+-export_type(['Names'/0, 'Package'/0]).
+
+-spec encode_msg('Names'() | 'Package'(), atom()) -> binary().
+encode_msg(Msg, MsgName) when is_atom(MsgName) ->
+ encode_msg(Msg, MsgName, []).
+
+-spec encode_msg('Names'() | 'Package'(), atom(), list()) -> binary().
+encode_msg(Msg, MsgName, Opts) ->
+ verify_msg(Msg, MsgName, Opts),
+ TrUserData = proplists:get_value(user_data, Opts),
+ case MsgName of
+ 'Names' -> e_msg_Names(id(Msg, TrUserData), TrUserData);
+ 'Package' ->
+ e_msg_Package(id(Msg, TrUserData), TrUserData)
+ end.
+
+
+e_msg_Names(Msg, TrUserData) ->
+ e_msg_Names(Msg, <<>>, TrUserData).
+
+
+e_msg_Names(#{repository := F2} = M, Bin, TrUserData) ->
+ B1 = case M of
+ #{packages := F1} ->
+ TrF1 = id(F1, TrUserData),
+ if TrF1 == [] -> Bin;
+ true -> e_field_Names_packages(TrF1, Bin, TrUserData)
+ end;
+ _ -> Bin
+ end,
+ begin
+ TrF2 = id(F2, TrUserData),
+ e_type_string(TrF2, <<B1/binary, 18>>, TrUserData)
+ end.
+
+e_msg_Package(Msg, TrUserData) ->
+ e_msg_Package(Msg, <<>>, TrUserData).
+
+
+e_msg_Package(#{name := F1}, Bin, TrUserData) ->
+ begin
+ TrF1 = id(F1, TrUserData),
+ e_type_string(TrF1, <<Bin/binary, 10>>, TrUserData)
+ end.
+
+e_mfield_Names_packages(Msg, Bin, TrUserData) ->
+ SubBin = e_msg_Package(Msg, <<>>, TrUserData),
+ Bin2 = e_varint(byte_size(SubBin), Bin),
+ <<Bin2/binary, SubBin/binary>>.
+
+e_field_Names_packages([Elem | Rest], Bin,
+ TrUserData) ->
+ Bin2 = <<Bin/binary, 10>>,
+ Bin3 = e_mfield_Names_packages(id(Elem, TrUserData),
+ Bin2, TrUserData),
+ e_field_Names_packages(Rest, Bin3, TrUserData);
+e_field_Names_packages([], Bin, _TrUserData) -> Bin.
+
+-compile({nowarn_unused_function,e_type_sint/3}).
+e_type_sint(Value, Bin, _TrUserData) when Value >= 0 ->
+ e_varint(Value * 2, Bin);
+e_type_sint(Value, Bin, _TrUserData) ->
+ e_varint(Value * -2 - 1, Bin).
+
+-compile({nowarn_unused_function,e_type_int32/3}).
+e_type_int32(Value, Bin, _TrUserData)
+ when 0 =< Value, Value =< 127 ->
+ <<Bin/binary, Value>>;
+e_type_int32(Value, Bin, _TrUserData) ->
+ <<N:64/unsigned-native>> = <<Value:64/signed-native>>,
+ e_varint(N, Bin).
+
+-compile({nowarn_unused_function,e_type_int64/3}).
+e_type_int64(Value, Bin, _TrUserData)
+ when 0 =< Value, Value =< 127 ->
+ <<Bin/binary, Value>>;
+e_type_int64(Value, Bin, _TrUserData) ->
+ <<N:64/unsigned-native>> = <<Value:64/signed-native>>,
+ e_varint(N, Bin).
+
+-compile({nowarn_unused_function,e_type_bool/3}).
+e_type_bool(true, Bin, _TrUserData) ->
+ <<Bin/binary, 1>>;
+e_type_bool(false, Bin, _TrUserData) ->
+ <<Bin/binary, 0>>;
+e_type_bool(1, Bin, _TrUserData) -> <<Bin/binary, 1>>;
+e_type_bool(0, Bin, _TrUserData) -> <<Bin/binary, 0>>.
+
+-compile({nowarn_unused_function,e_type_string/3}).
+e_type_string(S, Bin, _TrUserData) ->
+ Utf8 = unicode:characters_to_binary(S),
+ Bin2 = e_varint(byte_size(Utf8), Bin),
+ <<Bin2/binary, Utf8/binary>>.
+
+-compile({nowarn_unused_function,e_type_bytes/3}).
+e_type_bytes(Bytes, Bin, _TrUserData)
+ when is_binary(Bytes) ->
+ Bin2 = e_varint(byte_size(Bytes), Bin),
+ <<Bin2/binary, Bytes/binary>>;
+e_type_bytes(Bytes, Bin, _TrUserData)
+ when is_list(Bytes) ->
+ BytesBin = iolist_to_binary(Bytes),
+ Bin2 = e_varint(byte_size(BytesBin), Bin),
+ <<Bin2/binary, BytesBin/binary>>.
+
+-compile({nowarn_unused_function,e_type_fixed32/3}).
+e_type_fixed32(Value, Bin, _TrUserData) ->
+ <<Bin/binary, Value:32/little>>.
+
+-compile({nowarn_unused_function,e_type_sfixed32/3}).
+e_type_sfixed32(Value, Bin, _TrUserData) ->
+ <<Bin/binary, Value:32/little-signed>>.
+
+-compile({nowarn_unused_function,e_type_fixed64/3}).
+e_type_fixed64(Value, Bin, _TrUserData) ->
+ <<Bin/binary, Value:64/little>>.
+
+-compile({nowarn_unused_function,e_type_sfixed64/3}).
+e_type_sfixed64(Value, Bin, _TrUserData) ->
+ <<Bin/binary, Value:64/little-signed>>.
+
+-compile({nowarn_unused_function,e_type_float/3}).
+e_type_float(V, Bin, _) when is_number(V) ->
+ <<Bin/binary, V:32/little-float>>;
+e_type_float(infinity, Bin, _) ->
+ <<Bin/binary, 0:16, 128, 127>>;
+e_type_float('-infinity', Bin, _) ->
+ <<Bin/binary, 0:16, 128, 255>>;
+e_type_float(nan, Bin, _) ->
+ <<Bin/binary, 0:16, 192, 127>>.
+
+-compile({nowarn_unused_function,e_type_double/3}).
+e_type_double(V, Bin, _) when is_number(V) ->
+ <<Bin/binary, V:64/little-float>>;
+e_type_double(infinity, Bin, _) ->
+ <<Bin/binary, 0:48, 240, 127>>;
+e_type_double('-infinity', Bin, _) ->
+ <<Bin/binary, 0:48, 240, 255>>;
+e_type_double(nan, Bin, _) ->
+ <<Bin/binary, 0:48, 248, 127>>.
+
+-compile({nowarn_unused_function,e_varint/3}).
+e_varint(N, Bin, _TrUserData) -> e_varint(N, Bin).
+
+-compile({nowarn_unused_function,e_varint/2}).
+e_varint(N, Bin) when N =< 127 -> <<Bin/binary, N>>;
+e_varint(N, Bin) ->
+ Bin2 = <<Bin/binary, (N band 127 bor 128)>>,
+ e_varint(N bsr 7, Bin2).
+
+
+decode_msg(Bin, MsgName) when is_binary(Bin) ->
+ decode_msg(Bin, MsgName, []).
+
+decode_msg(Bin, MsgName, Opts) when is_binary(Bin) ->
+ TrUserData = proplists:get_value(user_data, Opts),
+ decode_msg_1_catch(Bin, MsgName, TrUserData).
+
+-ifdef('OTP_RELEASE').
+decode_msg_1_catch(Bin, MsgName, TrUserData) ->
+ try decode_msg_2_doit(MsgName, Bin, TrUserData)
+ catch Class:Reason:StackTrace -> error({gpb_error,{decoding_failure, {Bin, MsgName, {Class, Reason, StackTrace}}}})
+ end.
+-else.
+-ifdef('GPB_PATTERN_STACK').
+decode_msg_1_catch(Bin, MsgName, TrUserData) ->
+ try decode_msg_2_doit(MsgName, Bin, TrUserData)
+ catch Class:Reason:StackTrace -> error({gpb_error,{decoding_failure, {Bin, MsgName, {Class, Reason, StackTrace}}}})
+ end.
+-else.
+decode_msg_1_catch(Bin, MsgName, TrUserData) ->
+ try decode_msg_2_doit(MsgName, Bin, TrUserData)
+ catch Class:Reason ->
+ StackTrace = erlang:get_stacktrace(),
+ error({gpb_error,{decoding_failure, {Bin, MsgName, {Class, Reason, StackTrace}}}})
+ end.
+-endif.
+
+-endif.
+
+decode_msg_2_doit('Names', Bin, TrUserData) ->
+ id(d_msg_Names(Bin, TrUserData), TrUserData);
+decode_msg_2_doit('Package', Bin, TrUserData) ->
+ id(d_msg_Package(Bin, TrUserData), TrUserData).
+
+
+
+d_msg_Names(Bin, TrUserData) ->
+ dfp_read_field_def_Names(Bin, 0, 0, id([], TrUserData),
+ id('$undef', TrUserData), TrUserData).
+
+dfp_read_field_def_Names(<<10, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, TrUserData) ->
+ d_field_Names_packages(Rest, Z1, Z2, F@_1, F@_2,
+ TrUserData);
+dfp_read_field_def_Names(<<18, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, TrUserData) ->
+ d_field_Names_repository(Rest, Z1, Z2, F@_1, F@_2,
+ TrUserData);
+dfp_read_field_def_Names(<<>>, 0, 0, R1, F@_2,
+ TrUserData) ->
+ S1 = #{repository => F@_2},
+ if R1 == '$undef' -> S1;
+ true -> S1#{packages => lists_reverse(R1, TrUserData)}
+ end;
+dfp_read_field_def_Names(Other, Z1, Z2, F@_1, F@_2,
+ TrUserData) ->
+ dg_read_field_def_Names(Other, Z1, Z2, F@_1, F@_2,
+ TrUserData).
+
+dg_read_field_def_Names(<<1:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, TrUserData)
+ when N < 32 - 7 ->
+ dg_read_field_def_Names(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, TrUserData);
+dg_read_field_def_Names(<<0:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, TrUserData) ->
+ Key = X bsl N + Acc,
+ case Key of
+ 10 ->
+ d_field_Names_packages(Rest, 0, 0, F@_1, F@_2,
+ TrUserData);
+ 18 ->
+ d_field_Names_repository(Rest, 0, 0, F@_1, F@_2,
+ TrUserData);
+ _ ->
+ case Key band 7 of
+ 0 ->
+ skip_varint_Names(Rest, 0, 0, F@_1, F@_2, TrUserData);
+ 1 -> skip_64_Names(Rest, 0, 0, F@_1, F@_2, TrUserData);
+ 2 ->
+ skip_length_delimited_Names(Rest, 0, 0, F@_1, F@_2,
+ TrUserData);
+ 3 ->
+ skip_group_Names(Rest, Key bsr 3, 0, F@_1, F@_2,
+ TrUserData);
+ 5 -> skip_32_Names(Rest, 0, 0, F@_1, F@_2, TrUserData)
+ end
+ end;
+dg_read_field_def_Names(<<>>, 0, 0, R1, F@_2,
+ TrUserData) ->
+ S1 = #{repository => F@_2},
+ if R1 == '$undef' -> S1;
+ true -> S1#{packages => lists_reverse(R1, TrUserData)}
+ end.
+
+d_field_Names_packages(<<1:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, TrUserData)
+ when N < 57 ->
+ d_field_Names_packages(Rest, N + 7, X bsl N + Acc, F@_1,
+ F@_2, TrUserData);
+d_field_Names_packages(<<0:1, X:7, Rest/binary>>, N,
+ Acc, Prev, F@_2, TrUserData) ->
+ {NewFValue, RestF} = begin
+ Len = X bsl N + Acc,
+ <<Bs:Len/binary, Rest2/binary>> = Rest,
+ {id(d_msg_Package(Bs, TrUserData), TrUserData),
+ Rest2}
+ end,
+ dfp_read_field_def_Names(RestF, 0, 0,
+ cons(NewFValue, Prev, TrUserData), F@_2,
+ TrUserData).
+
+d_field_Names_repository(<<1:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, TrUserData)
+ when N < 57 ->
+ d_field_Names_repository(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, TrUserData);
+d_field_Names_repository(<<0:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, _, TrUserData) ->
+ {NewFValue, RestF} = begin
+ Len = X bsl N + Acc,
+ <<Bytes:Len/binary, Rest2/binary>> = Rest,
+ {id(binary:copy(Bytes), TrUserData), Rest2}
+ end,
+ dfp_read_field_def_Names(RestF, 0, 0, F@_1, NewFValue,
+ TrUserData).
+
+skip_varint_Names(<<1:1, _:7, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, TrUserData) ->
+ skip_varint_Names(Rest, Z1, Z2, F@_1, F@_2, TrUserData);
+skip_varint_Names(<<0:1, _:7, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, TrUserData) ->
+ dfp_read_field_def_Names(Rest, Z1, Z2, F@_1, F@_2,
+ TrUserData).
+
+skip_length_delimited_Names(<<1:1, X:7, Rest/binary>>,
+ N, Acc, F@_1, F@_2, TrUserData)
+ when N < 57 ->
+ skip_length_delimited_Names(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, TrUserData);
+skip_length_delimited_Names(<<0:1, X:7, Rest/binary>>,
+ N, Acc, F@_1, F@_2, TrUserData) ->
+ Length = X bsl N + Acc,
+ <<_:Length/binary, Rest2/binary>> = Rest,
+ dfp_read_field_def_Names(Rest2, 0, 0, F@_1, F@_2,
+ TrUserData).
+
+skip_group_Names(Bin, FNum, Z2, F@_1, F@_2,
+ TrUserData) ->
+ {_, Rest} = read_group(Bin, FNum),
+ dfp_read_field_def_Names(Rest, 0, Z2, F@_1, F@_2,
+ TrUserData).
+
+skip_32_Names(<<_:32, Rest/binary>>, Z1, Z2, F@_1, F@_2,
+ TrUserData) ->
+ dfp_read_field_def_Names(Rest, Z1, Z2, F@_1, F@_2,
+ TrUserData).
+
+skip_64_Names(<<_:64, Rest/binary>>, Z1, Z2, F@_1, F@_2,
+ TrUserData) ->
+ dfp_read_field_def_Names(Rest, Z1, Z2, F@_1, F@_2,
+ TrUserData).
+
+d_msg_Package(Bin, TrUserData) ->
+ dfp_read_field_def_Package(Bin, 0, 0,
+ id('$undef', TrUserData), TrUserData).
+
+dfp_read_field_def_Package(<<10, Rest/binary>>, Z1, Z2,
+ F@_1, TrUserData) ->
+ d_field_Package_name(Rest, Z1, Z2, F@_1, TrUserData);
+dfp_read_field_def_Package(<<>>, 0, 0, F@_1, _) ->
+ #{name => F@_1};
+dfp_read_field_def_Package(Other, Z1, Z2, F@_1,
+ TrUserData) ->
+ dg_read_field_def_Package(Other, Z1, Z2, F@_1,
+ TrUserData).
+
+dg_read_field_def_Package(<<1:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, TrUserData)
+ when N < 32 - 7 ->
+ dg_read_field_def_Package(Rest, N + 7, X bsl N + Acc,
+ F@_1, TrUserData);
+dg_read_field_def_Package(<<0:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, TrUserData) ->
+ Key = X bsl N + Acc,
+ case Key of
+ 10 ->
+ d_field_Package_name(Rest, 0, 0, F@_1, TrUserData);
+ _ ->
+ case Key band 7 of
+ 0 -> skip_varint_Package(Rest, 0, 0, F@_1, TrUserData);
+ 1 -> skip_64_Package(Rest, 0, 0, F@_1, TrUserData);
+ 2 ->
+ skip_length_delimited_Package(Rest, 0, 0, F@_1,
+ TrUserData);
+ 3 ->
+ skip_group_Package(Rest, Key bsr 3, 0, F@_1,
+ TrUserData);
+ 5 -> skip_32_Package(Rest, 0, 0, F@_1, TrUserData)
+ end
+ end;
+dg_read_field_def_Package(<<>>, 0, 0, F@_1, _) ->
+ #{name => F@_1}.
+
+d_field_Package_name(<<1:1, X:7, Rest/binary>>, N, Acc,
+ F@_1, TrUserData)
+ when N < 57 ->
+ d_field_Package_name(Rest, N + 7, X bsl N + Acc, F@_1,
+ TrUserData);
+d_field_Package_name(<<0:1, X:7, Rest/binary>>, N, Acc,
+ _, TrUserData) ->
+ {NewFValue, RestF} = begin
+ Len = X bsl N + Acc,
+ <<Bytes:Len/binary, Rest2/binary>> = Rest,
+ {id(binary:copy(Bytes), TrUserData), Rest2}
+ end,
+ dfp_read_field_def_Package(RestF, 0, 0, NewFValue,
+ TrUserData).
+
+skip_varint_Package(<<1:1, _:7, Rest/binary>>, Z1, Z2,
+ F@_1, TrUserData) ->
+ skip_varint_Package(Rest, Z1, Z2, F@_1, TrUserData);
+skip_varint_Package(<<0:1, _:7, Rest/binary>>, Z1, Z2,
+ F@_1, TrUserData) ->
+ dfp_read_field_def_Package(Rest, Z1, Z2, F@_1,
+ TrUserData).
+
+skip_length_delimited_Package(<<1:1, X:7, Rest/binary>>,
+ N, Acc, F@_1, TrUserData)
+ when N < 57 ->
+ skip_length_delimited_Package(Rest, N + 7,
+ X bsl N + Acc, F@_1, TrUserData);
+skip_length_delimited_Package(<<0:1, X:7, Rest/binary>>,
+ N, Acc, F@_1, TrUserData) ->
+ Length = X bsl N + Acc,
+ <<_:Length/binary, Rest2/binary>> = Rest,
+ dfp_read_field_def_Package(Rest2, 0, 0, F@_1,
+ TrUserData).
+
+skip_group_Package(Bin, FNum, Z2, F@_1, TrUserData) ->
+ {_, Rest} = read_group(Bin, FNum),
+ dfp_read_field_def_Package(Rest, 0, Z2, F@_1,
+ TrUserData).
+
+skip_32_Package(<<_:32, Rest/binary>>, Z1, Z2, F@_1,
+ TrUserData) ->
+ dfp_read_field_def_Package(Rest, Z1, Z2, F@_1,
+ TrUserData).
+
+skip_64_Package(<<_:64, Rest/binary>>, Z1, Z2, F@_1,
+ TrUserData) ->
+ dfp_read_field_def_Package(Rest, Z1, Z2, F@_1,
+ TrUserData).
+
+read_group(Bin, FieldNum) ->
+ {NumBytes, EndTagLen} = read_gr_b(Bin, 0, 0, 0, 0, FieldNum),
+ <<Group:NumBytes/binary, _:EndTagLen/binary, Rest/binary>> = Bin,
+ {Group, Rest}.
+
+%% Like skipping over fields, but record the total length,
+%% Each field is <(FieldNum bsl 3) bor FieldType> ++ <FieldValue>
+%% Record the length because varints may be non-optimally encoded.
+%%
+%% Groups can be nested, but assume the same FieldNum cannot be nested
+%% because group field numbers are shared with the rest of the fields
+%% numbers. Thus we can search just for an group-end with the same
+%% field number.
+%%
+%% (The only time the same group field number could occur would
+%% be in a nested sub message, but then it would be inside a
+%% length-delimited entry, which we skip-read by length.)
+read_gr_b(<<1:1, X:7, Tl/binary>>, N, Acc, NumBytes, TagLen, FieldNum)
+ when N < (32-7) ->
+ read_gr_b(Tl, N+7, X bsl N + Acc, NumBytes, TagLen+1, FieldNum);
+read_gr_b(<<0:1, X:7, Tl/binary>>, N, Acc, NumBytes, TagLen,
+ FieldNum) ->
+ Key = X bsl N + Acc,
+ TagLen1 = TagLen + 1,
+ case {Key bsr 3, Key band 7} of
+ {FieldNum, 4} -> % 4 = group_end
+ {NumBytes, TagLen1};
+ {_, 0} -> % 0 = varint
+ read_gr_vi(Tl, 0, NumBytes + TagLen1, FieldNum);
+ {_, 1} -> % 1 = bits64
+ <<_:64, Tl2/binary>> = Tl,
+ read_gr_b(Tl2, 0, 0, NumBytes + TagLen1 + 8, 0, FieldNum);
+ {_, 2} -> % 2 = length_delimited
+ read_gr_ld(Tl, 0, 0, NumBytes + TagLen1, FieldNum);
+ {_, 3} -> % 3 = group_start
+ read_gr_b(Tl, 0, 0, NumBytes + TagLen1, 0, FieldNum);
+ {_, 4} -> % 4 = group_end
+ read_gr_b(Tl, 0, 0, NumBytes + TagLen1, 0, FieldNum);
+ {_, 5} -> % 5 = bits32
+ <<_:32, Tl2/binary>> = Tl,
+ read_gr_b(Tl2, 0, 0, NumBytes + TagLen1 + 4, 0, FieldNum)
+ end.
+
+read_gr_vi(<<1:1, _:7, Tl/binary>>, N, NumBytes, FieldNum)
+ when N < (64-7) ->
+ read_gr_vi(Tl, N+7, NumBytes+1, FieldNum);
+read_gr_vi(<<0:1, _:7, Tl/binary>>, _, NumBytes, FieldNum) ->
+ read_gr_b(Tl, 0, 0, NumBytes+1, 0, FieldNum).
+
+read_gr_ld(<<1:1, X:7, Tl/binary>>, N, Acc, NumBytes, FieldNum)
+ when N < (64-7) ->
+ read_gr_ld(Tl, N+7, X bsl N + Acc, NumBytes+1, FieldNum);
+read_gr_ld(<<0:1, X:7, Tl/binary>>, N, Acc, NumBytes, FieldNum) ->
+ Len = X bsl N + Acc,
+ NumBytes1 = NumBytes + 1,
+ <<_:Len/binary, Tl2/binary>> = Tl,
+ read_gr_b(Tl2, 0, 0, NumBytes1 + Len, 0, FieldNum).
+
+merge_msgs(Prev, New, MsgName) when is_atom(MsgName) ->
+ merge_msgs(Prev, New, MsgName, []).
+
+merge_msgs(Prev, New, MsgName, Opts) ->
+ TrUserData = proplists:get_value(user_data, Opts),
+ case MsgName of
+ 'Names' -> merge_msg_Names(Prev, New, TrUserData);
+ 'Package' -> merge_msg_Package(Prev, New, TrUserData)
+ end.
+
+-compile({nowarn_unused_function,merge_msg_Names/3}).
+merge_msg_Names(#{} = PMsg,
+ #{repository := NFrepository} = NMsg, TrUserData) ->
+ S1 = #{repository => NFrepository},
+ case {PMsg, NMsg} of
+ {#{packages := PFpackages},
+ #{packages := NFpackages}} ->
+ S1#{packages =>
+ 'erlang_++'(PFpackages, NFpackages, TrUserData)};
+ {_, #{packages := NFpackages}} ->
+ S1#{packages => NFpackages};
+ {#{packages := PFpackages}, _} ->
+ S1#{packages => PFpackages};
+ {_, _} -> S1
+ end.
+
+-compile({nowarn_unused_function,merge_msg_Package/3}).
+merge_msg_Package(#{}, #{name := NFname}, _) ->
+ #{name => NFname}.
+
+
+verify_msg(Msg, MsgName) when is_atom(MsgName) ->
+ verify_msg(Msg, MsgName, []).
+
+verify_msg(Msg, MsgName, Opts) ->
+ TrUserData = proplists:get_value(user_data, Opts),
+ case MsgName of
+ 'Names' -> v_msg_Names(Msg, [MsgName], TrUserData);
+ 'Package' -> v_msg_Package(Msg, [MsgName], TrUserData);
+ _ -> mk_type_error(not_a_known_message, Msg, [])
+ end.
+
+
+-compile({nowarn_unused_function,v_msg_Names/3}).
+v_msg_Names(#{repository := F2} = M, Path,
+ TrUserData) ->
+ case M of
+ #{packages := F1} ->
+ if is_list(F1) ->
+ _ = [v_msg_Package(Elem, [packages | Path], TrUserData)
+ || Elem <- F1],
+ ok;
+ true ->
+ mk_type_error({invalid_list_of, {msg, 'Package'}}, F1,
+ [packages | Path])
+ end;
+ _ -> ok
+ end,
+ v_type_string(F2, [repository | Path], TrUserData),
+ lists:foreach(fun (packages) -> ok;
+ (repository) -> ok;
+ (OtherKey) ->
+ mk_type_error({extraneous_key, OtherKey}, M, Path)
+ end,
+ maps:keys(M)),
+ ok;
+v_msg_Names(M, Path, _TrUserData) when is_map(M) ->
+ mk_type_error({missing_fields,
+ [repository] -- maps:keys(M), 'Names'},
+ M, Path);
+v_msg_Names(X, Path, _TrUserData) ->
+ mk_type_error({expected_msg, 'Names'}, X, Path).
+
+-compile({nowarn_unused_function,v_msg_Package/3}).
+v_msg_Package(#{name := F1} = M, Path, TrUserData) ->
+ v_type_string(F1, [name | Path], TrUserData),
+ lists:foreach(fun (name) -> ok;
+ (OtherKey) ->
+ mk_type_error({extraneous_key, OtherKey}, M, Path)
+ end,
+ maps:keys(M)),
+ ok;
+v_msg_Package(M, Path, _TrUserData) when is_map(M) ->
+ mk_type_error({missing_fields, [name] -- maps:keys(M),
+ 'Package'},
+ M, Path);
+v_msg_Package(X, Path, _TrUserData) ->
+ mk_type_error({expected_msg, 'Package'}, X, Path).
+
+-compile({nowarn_unused_function,v_type_string/3}).
+v_type_string(S, Path, _TrUserData)
+ when is_list(S); is_binary(S) ->
+ try unicode:characters_to_binary(S) of
+ B when is_binary(B) -> ok;
+ {error, _, _} ->
+ mk_type_error(bad_unicode_string, S, Path)
+ catch
+ error:badarg ->
+ mk_type_error(bad_unicode_string, S, Path)
+ end;
+v_type_string(X, Path, _TrUserData) ->
+ mk_type_error(bad_unicode_string, X, Path).
+
+-compile({nowarn_unused_function,mk_type_error/3}).
+-spec mk_type_error(_, _, list()) -> no_return().
+mk_type_error(Error, ValueSeen, Path) ->
+ Path2 = prettify_path(Path),
+ erlang:error({gpb_type_error,
+ {Error, [{value, ValueSeen}, {path, Path2}]}}).
+
+
+-compile({nowarn_unused_function,prettify_path/1}).
+prettify_path([]) -> top_level;
+prettify_path(PathR) ->
+ list_to_atom(string:join(lists:map(fun atom_to_list/1,
+ lists:reverse(PathR)),
+ ".")).
+
+
+-compile({nowarn_unused_function,id/2}).
+-compile({inline,id/2}).
+id(X, _TrUserData) -> X.
+
+-compile({nowarn_unused_function,v_ok/3}).
+-compile({inline,v_ok/3}).
+v_ok(_Value, _Path, _TrUserData) -> ok.
+
+-compile({nowarn_unused_function,m_overwrite/3}).
+-compile({inline,m_overwrite/3}).
+m_overwrite(_Prev, New, _TrUserData) -> New.
+
+-compile({nowarn_unused_function,cons/3}).
+-compile({inline,cons/3}).
+cons(Elem, Acc, _TrUserData) -> [Elem | Acc].
+
+-compile({nowarn_unused_function,lists_reverse/2}).
+-compile({inline,lists_reverse/2}).
+'lists_reverse'(L, _TrUserData) -> lists:reverse(L).
+-compile({nowarn_unused_function,'erlang_++'/3}).
+-compile({inline,'erlang_++'/3}).
+'erlang_++'(A, B, _TrUserData) -> A ++ B.
+
+get_msg_defs() ->
+ [{{msg, 'Names'},
+ [#{name => packages, fnum => 1, rnum => 2,
+ type => {msg, 'Package'}, occurrence => repeated,
+ opts => []},
+ #{name => repository, fnum => 2, rnum => 3,
+ type => string, occurrence => required, opts => []}]},
+ {{msg, 'Package'},
+ [#{name => name, fnum => 1, rnum => 2, type => string,
+ occurrence => required, opts => []}]}].
+
+
+get_msg_names() -> ['Names', 'Package'].
+
+
+get_group_names() -> [].
+
+
+get_msg_or_group_names() -> ['Names', 'Package'].
+
+
+get_enum_names() -> [].
+
+
+fetch_msg_def(MsgName) ->
+ case find_msg_def(MsgName) of
+ Fs when is_list(Fs) -> Fs;
+ error -> erlang:error({no_such_msg, MsgName})
+ end.
+
+
+-spec fetch_enum_def(_) -> no_return().
+fetch_enum_def(EnumName) ->
+ erlang:error({no_such_enum, EnumName}).
+
+
+find_msg_def('Names') ->
+ [#{name => packages, fnum => 1, rnum => 2,
+ type => {msg, 'Package'}, occurrence => repeated,
+ opts => []},
+ #{name => repository, fnum => 2, rnum => 3,
+ type => string, occurrence => required, opts => []}];
+find_msg_def('Package') ->
+ [#{name => name, fnum => 1, rnum => 2, type => string,
+ occurrence => required, opts => []}];
+find_msg_def(_) -> error.
+
+
+find_enum_def(_) -> error.
+
+
+-spec enum_symbol_by_value(_, _) -> no_return().
+enum_symbol_by_value(E, V) ->
+ erlang:error({no_enum_defs, E, V}).
+
+
+-spec enum_value_by_symbol(_, _) -> no_return().
+enum_value_by_symbol(E, V) ->
+ erlang:error({no_enum_defs, E, V}).
+
+
+
+get_service_names() -> [].
+
+
+get_service_def(_) -> error.
+
+
+get_rpc_names(_) -> error.
+
+
+find_rpc_def(_, _) -> error.
+
+
+
+-spec fetch_rpc_def(_, _) -> no_return().
+fetch_rpc_def(ServiceName, RpcName) ->
+ erlang:error({no_such_rpc, ServiceName, RpcName}).
+
+
+get_package_name() -> undefined.
+
+
+
+gpb_version_as_string() ->
+ "4.3.1".
+
+gpb_version_as_list() ->
+ [4,3,1].
diff --git a/src/r3_hex_pb_package.erl b/src/r3_hex_pb_package.erl
new file mode 100644
index 0000000..131a5da
--- /dev/null
+++ b/src/r3_hex_pb_package.erl
@@ -0,0 +1,1699 @@
+%% Vendored from hex_core v0.5.0, do not edit manually
+
+%% -*- coding: utf-8 -*-
+%% Automatically generated, do not edit
+%% Generated by gpb_compile version 4.3.1
+-module(r3_hex_pb_package).
+
+-export([encode_msg/2, encode_msg/3]).
+-export([decode_msg/2, decode_msg/3]).
+-export([merge_msgs/3, merge_msgs/4]).
+-export([verify_msg/2, verify_msg/3]).
+-export([get_msg_defs/0]).
+-export([get_msg_names/0]).
+-export([get_group_names/0]).
+-export([get_msg_or_group_names/0]).
+-export([get_enum_names/0]).
+-export([find_msg_def/1, fetch_msg_def/1]).
+-export([find_enum_def/1, fetch_enum_def/1]).
+-export([enum_symbol_by_value/2, enum_value_by_symbol/2]).
+-export([enum_symbol_by_value_RetirementReason/1, enum_value_by_symbol_RetirementReason/1]).
+-export([get_service_names/0]).
+-export([get_service_def/1]).
+-export([get_rpc_names/1]).
+-export([find_rpc_def/2, fetch_rpc_def/2]).
+-export([get_package_name/0]).
+-export([gpb_version_as_string/0, gpb_version_as_list/0]).
+
+
+%% enumerated types
+-type 'RetirementReason'() :: 'RETIRED_OTHER' | 'RETIRED_INVALID' | 'RETIRED_SECURITY' | 'RETIRED_DEPRECATED' | 'RETIRED_RENAMED'.
+-export_type(['RetirementReason'/0]).
+
+%% message types
+-type 'Package'() ::
+ #{releases => ['Release'()], % = 1
+ name => iodata(), % = 2
+ repository => iodata() % = 3
+ }.
+
+-type 'Release'() ::
+ #{version => iodata(), % = 1
+ checksum => iodata(), % = 2
+ dependencies => ['Dependency'()] % = 3
+ %% retired => 'RetirementStatus'() % = 4
+ }.
+
+-type 'RetirementStatus'() ::
+ #{reason => 'RETIRED_OTHER' | 'RETIRED_INVALID' | 'RETIRED_SECURITY' | 'RETIRED_DEPRECATED' | 'RETIRED_RENAMED' | integer() % = 1, enum RetirementReason
+ %% message => iodata() % = 2
+ }.
+
+-type 'Dependency'() ::
+ #{package => iodata(), % = 1
+ requirement => iodata() % = 2
+ %% optional => boolean() | 0 | 1 % = 3
+ %% app => iodata() % = 4
+ %% repository => iodata() % = 5
+ }.
+
+-export_type(['Package'/0, 'Release'/0, 'RetirementStatus'/0, 'Dependency'/0]).
+
+-spec encode_msg('Package'() | 'Release'() | 'RetirementStatus'() | 'Dependency'(), atom()) -> binary().
+encode_msg(Msg, MsgName) when is_atom(MsgName) ->
+ encode_msg(Msg, MsgName, []).
+
+-spec encode_msg('Package'() | 'Release'() | 'RetirementStatus'() | 'Dependency'(), atom(), list()) -> binary().
+encode_msg(Msg, MsgName, Opts) ->
+ verify_msg(Msg, MsgName, Opts),
+ TrUserData = proplists:get_value(user_data, Opts),
+ case MsgName of
+ 'Package' ->
+ e_msg_Package(id(Msg, TrUserData), TrUserData);
+ 'Release' ->
+ e_msg_Release(id(Msg, TrUserData), TrUserData);
+ 'RetirementStatus' ->
+ e_msg_RetirementStatus(id(Msg, TrUserData), TrUserData);
+ 'Dependency' ->
+ e_msg_Dependency(id(Msg, TrUserData), TrUserData)
+ end.
+
+
+e_msg_Package(Msg, TrUserData) ->
+ e_msg_Package(Msg, <<>>, TrUserData).
+
+
+e_msg_Package(#{name := F2, repository := F3} = M, Bin,
+ TrUserData) ->
+ B1 = case M of
+ #{releases := F1} ->
+ TrF1 = id(F1, TrUserData),
+ if TrF1 == [] -> Bin;
+ true -> e_field_Package_releases(TrF1, Bin, TrUserData)
+ end;
+ _ -> Bin
+ end,
+ B2 = begin
+ TrF2 = id(F2, TrUserData),
+ e_type_string(TrF2, <<B1/binary, 18>>, TrUserData)
+ end,
+ begin
+ TrF3 = id(F3, TrUserData),
+ e_type_string(TrF3, <<B2/binary, 26>>, TrUserData)
+ end.
+
+e_msg_Release(Msg, TrUserData) ->
+ e_msg_Release(Msg, <<>>, TrUserData).
+
+
+e_msg_Release(#{version := F1, checksum := F2} = M, Bin,
+ TrUserData) ->
+ B1 = begin
+ TrF1 = id(F1, TrUserData),
+ e_type_string(TrF1, <<Bin/binary, 10>>, TrUserData)
+ end,
+ B2 = begin
+ TrF2 = id(F2, TrUserData),
+ e_type_bytes(TrF2, <<B1/binary, 18>>, TrUserData)
+ end,
+ B3 = case M of
+ #{dependencies := F3} ->
+ TrF3 = id(F3, TrUserData),
+ if TrF3 == [] -> B2;
+ true ->
+ e_field_Release_dependencies(TrF3, B2, TrUserData)
+ end;
+ _ -> B2
+ end,
+ case M of
+ #{retired := F4} ->
+ begin
+ TrF4 = id(F4, TrUserData),
+ e_mfield_Release_retired(TrF4, <<B3/binary, 34>>,
+ TrUserData)
+ end;
+ _ -> B3
+ end.
+
+e_msg_RetirementStatus(Msg, TrUserData) ->
+ e_msg_RetirementStatus(Msg, <<>>, TrUserData).
+
+
+e_msg_RetirementStatus(#{reason := F1} = M, Bin,
+ TrUserData) ->
+ B1 = begin
+ TrF1 = id(F1, TrUserData),
+ e_enum_RetirementReason(TrF1, <<Bin/binary, 8>>,
+ TrUserData)
+ end,
+ case M of
+ #{message := F2} ->
+ begin
+ TrF2 = id(F2, TrUserData),
+ e_type_string(TrF2, <<B1/binary, 18>>, TrUserData)
+ end;
+ _ -> B1
+ end.
+
+e_msg_Dependency(Msg, TrUserData) ->
+ e_msg_Dependency(Msg, <<>>, TrUserData).
+
+
+e_msg_Dependency(#{package := F1, requirement := F2} =
+ M,
+ Bin, TrUserData) ->
+ B1 = begin
+ TrF1 = id(F1, TrUserData),
+ e_type_string(TrF1, <<Bin/binary, 10>>, TrUserData)
+ end,
+ B2 = begin
+ TrF2 = id(F2, TrUserData),
+ e_type_string(TrF2, <<B1/binary, 18>>, TrUserData)
+ end,
+ B3 = case M of
+ #{optional := F3} ->
+ begin
+ TrF3 = id(F3, TrUserData),
+ e_type_bool(TrF3, <<B2/binary, 24>>, TrUserData)
+ end;
+ _ -> B2
+ end,
+ B4 = case M of
+ #{app := F4} ->
+ begin
+ TrF4 = id(F4, TrUserData),
+ e_type_string(TrF4, <<B3/binary, 34>>, TrUserData)
+ end;
+ _ -> B3
+ end,
+ case M of
+ #{repository := F5} ->
+ begin
+ TrF5 = id(F5, TrUserData),
+ e_type_string(TrF5, <<B4/binary, 42>>, TrUserData)
+ end;
+ _ -> B4
+ end.
+
+e_mfield_Package_releases(Msg, Bin, TrUserData) ->
+ SubBin = e_msg_Release(Msg, <<>>, TrUserData),
+ Bin2 = e_varint(byte_size(SubBin), Bin),
+ <<Bin2/binary, SubBin/binary>>.
+
+e_field_Package_releases([Elem | Rest], Bin,
+ TrUserData) ->
+ Bin2 = <<Bin/binary, 10>>,
+ Bin3 = e_mfield_Package_releases(id(Elem, TrUserData),
+ Bin2, TrUserData),
+ e_field_Package_releases(Rest, Bin3, TrUserData);
+e_field_Package_releases([], Bin, _TrUserData) -> Bin.
+
+e_mfield_Release_dependencies(Msg, Bin, TrUserData) ->
+ SubBin = e_msg_Dependency(Msg, <<>>, TrUserData),
+ Bin2 = e_varint(byte_size(SubBin), Bin),
+ <<Bin2/binary, SubBin/binary>>.
+
+e_field_Release_dependencies([Elem | Rest], Bin,
+ TrUserData) ->
+ Bin2 = <<Bin/binary, 26>>,
+ Bin3 = e_mfield_Release_dependencies(id(Elem,
+ TrUserData),
+ Bin2, TrUserData),
+ e_field_Release_dependencies(Rest, Bin3, TrUserData);
+e_field_Release_dependencies([], Bin, _TrUserData) ->
+ Bin.
+
+e_mfield_Release_retired(Msg, Bin, TrUserData) ->
+ SubBin = e_msg_RetirementStatus(Msg, <<>>, TrUserData),
+ Bin2 = e_varint(byte_size(SubBin), Bin),
+ <<Bin2/binary, SubBin/binary>>.
+
+e_enum_RetirementReason('RETIRED_OTHER', Bin,
+ _TrUserData) ->
+ <<Bin/binary, 0>>;
+e_enum_RetirementReason('RETIRED_INVALID', Bin,
+ _TrUserData) ->
+ <<Bin/binary, 1>>;
+e_enum_RetirementReason('RETIRED_SECURITY', Bin,
+ _TrUserData) ->
+ <<Bin/binary, 2>>;
+e_enum_RetirementReason('RETIRED_DEPRECATED', Bin,
+ _TrUserData) ->
+ <<Bin/binary, 3>>;
+e_enum_RetirementReason('RETIRED_RENAMED', Bin,
+ _TrUserData) ->
+ <<Bin/binary, 4>>;
+e_enum_RetirementReason(V, Bin, _TrUserData) ->
+ e_varint(V, Bin).
+
+-compile({nowarn_unused_function,e_type_sint/3}).
+e_type_sint(Value, Bin, _TrUserData) when Value >= 0 ->
+ e_varint(Value * 2, Bin);
+e_type_sint(Value, Bin, _TrUserData) ->
+ e_varint(Value * -2 - 1, Bin).
+
+-compile({nowarn_unused_function,e_type_int32/3}).
+e_type_int32(Value, Bin, _TrUserData)
+ when 0 =< Value, Value =< 127 ->
+ <<Bin/binary, Value>>;
+e_type_int32(Value, Bin, _TrUserData) ->
+ <<N:64/unsigned-native>> = <<Value:64/signed-native>>,
+ e_varint(N, Bin).
+
+-compile({nowarn_unused_function,e_type_int64/3}).
+e_type_int64(Value, Bin, _TrUserData)
+ when 0 =< Value, Value =< 127 ->
+ <<Bin/binary, Value>>;
+e_type_int64(Value, Bin, _TrUserData) ->
+ <<N:64/unsigned-native>> = <<Value:64/signed-native>>,
+ e_varint(N, Bin).
+
+-compile({nowarn_unused_function,e_type_bool/3}).
+e_type_bool(true, Bin, _TrUserData) ->
+ <<Bin/binary, 1>>;
+e_type_bool(false, Bin, _TrUserData) ->
+ <<Bin/binary, 0>>;
+e_type_bool(1, Bin, _TrUserData) -> <<Bin/binary, 1>>;
+e_type_bool(0, Bin, _TrUserData) -> <<Bin/binary, 0>>.
+
+-compile({nowarn_unused_function,e_type_string/3}).
+e_type_string(S, Bin, _TrUserData) ->
+ Utf8 = unicode:characters_to_binary(S),
+ Bin2 = e_varint(byte_size(Utf8), Bin),
+ <<Bin2/binary, Utf8/binary>>.
+
+-compile({nowarn_unused_function,e_type_bytes/3}).
+e_type_bytes(Bytes, Bin, _TrUserData)
+ when is_binary(Bytes) ->
+ Bin2 = e_varint(byte_size(Bytes), Bin),
+ <<Bin2/binary, Bytes/binary>>;
+e_type_bytes(Bytes, Bin, _TrUserData)
+ when is_list(Bytes) ->
+ BytesBin = iolist_to_binary(Bytes),
+ Bin2 = e_varint(byte_size(BytesBin), Bin),
+ <<Bin2/binary, BytesBin/binary>>.
+
+-compile({nowarn_unused_function,e_type_fixed32/3}).
+e_type_fixed32(Value, Bin, _TrUserData) ->
+ <<Bin/binary, Value:32/little>>.
+
+-compile({nowarn_unused_function,e_type_sfixed32/3}).
+e_type_sfixed32(Value, Bin, _TrUserData) ->
+ <<Bin/binary, Value:32/little-signed>>.
+
+-compile({nowarn_unused_function,e_type_fixed64/3}).
+e_type_fixed64(Value, Bin, _TrUserData) ->
+ <<Bin/binary, Value:64/little>>.
+
+-compile({nowarn_unused_function,e_type_sfixed64/3}).
+e_type_sfixed64(Value, Bin, _TrUserData) ->
+ <<Bin/binary, Value:64/little-signed>>.
+
+-compile({nowarn_unused_function,e_type_float/3}).
+e_type_float(V, Bin, _) when is_number(V) ->
+ <<Bin/binary, V:32/little-float>>;
+e_type_float(infinity, Bin, _) ->
+ <<Bin/binary, 0:16, 128, 127>>;
+e_type_float('-infinity', Bin, _) ->
+ <<Bin/binary, 0:16, 128, 255>>;
+e_type_float(nan, Bin, _) ->
+ <<Bin/binary, 0:16, 192, 127>>.
+
+-compile({nowarn_unused_function,e_type_double/3}).
+e_type_double(V, Bin, _) when is_number(V) ->
+ <<Bin/binary, V:64/little-float>>;
+e_type_double(infinity, Bin, _) ->
+ <<Bin/binary, 0:48, 240, 127>>;
+e_type_double('-infinity', Bin, _) ->
+ <<Bin/binary, 0:48, 240, 255>>;
+e_type_double(nan, Bin, _) ->
+ <<Bin/binary, 0:48, 248, 127>>.
+
+-compile({nowarn_unused_function,e_varint/3}).
+e_varint(N, Bin, _TrUserData) -> e_varint(N, Bin).
+
+-compile({nowarn_unused_function,e_varint/2}).
+e_varint(N, Bin) when N =< 127 -> <<Bin/binary, N>>;
+e_varint(N, Bin) ->
+ Bin2 = <<Bin/binary, (N band 127 bor 128)>>,
+ e_varint(N bsr 7, Bin2).
+
+
+decode_msg(Bin, MsgName) when is_binary(Bin) ->
+ decode_msg(Bin, MsgName, []).
+
+decode_msg(Bin, MsgName, Opts) when is_binary(Bin) ->
+ TrUserData = proplists:get_value(user_data, Opts),
+ decode_msg_1_catch(Bin, MsgName, TrUserData).
+
+-ifdef('OTP_RELEASE').
+decode_msg_1_catch(Bin, MsgName, TrUserData) ->
+ try decode_msg_2_doit(MsgName, Bin, TrUserData)
+ catch Class:Reason:StackTrace -> error({gpb_error,{decoding_failure, {Bin, MsgName, {Class, Reason, StackTrace}}}})
+ end.
+-else.
+-ifdef('GPB_PATTERN_STACK').
+decode_msg_1_catch(Bin, MsgName, TrUserData) ->
+ try decode_msg_2_doit(MsgName, Bin, TrUserData)
+ catch Class:Reason:StackTrace -> error({gpb_error,{decoding_failure, {Bin, MsgName, {Class, Reason, StackTrace}}}})
+ end.
+-else.
+decode_msg_1_catch(Bin, MsgName, TrUserData) ->
+ try decode_msg_2_doit(MsgName, Bin, TrUserData)
+ catch Class:Reason ->
+ StackTrace = erlang:get_stacktrace(),
+ error({gpb_error,{decoding_failure, {Bin, MsgName, {Class, Reason, StackTrace}}}})
+ end.
+-endif.
+
+-endif.
+
+decode_msg_2_doit('Package', Bin, TrUserData) ->
+ id(d_msg_Package(Bin, TrUserData), TrUserData);
+decode_msg_2_doit('Release', Bin, TrUserData) ->
+ id(d_msg_Release(Bin, TrUserData), TrUserData);
+decode_msg_2_doit('RetirementStatus', Bin,
+ TrUserData) ->
+ id(d_msg_RetirementStatus(Bin, TrUserData), TrUserData);
+decode_msg_2_doit('Dependency', Bin, TrUserData) ->
+ id(d_msg_Dependency(Bin, TrUserData), TrUserData).
+
+
+
+d_msg_Package(Bin, TrUserData) ->
+ dfp_read_field_def_Package(Bin, 0, 0,
+ id([], TrUserData), id('$undef', TrUserData),
+ id('$undef', TrUserData), TrUserData).
+
+dfp_read_field_def_Package(<<10, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, F@_3, TrUserData) ->
+ d_field_Package_releases(Rest, Z1, Z2, F@_1, F@_2, F@_3,
+ TrUserData);
+dfp_read_field_def_Package(<<18, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, F@_3, TrUserData) ->
+ d_field_Package_name(Rest, Z1, Z2, F@_1, F@_2, F@_3,
+ TrUserData);
+dfp_read_field_def_Package(<<26, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, F@_3, TrUserData) ->
+ d_field_Package_repository(Rest, Z1, Z2, F@_1, F@_2,
+ F@_3, TrUserData);
+dfp_read_field_def_Package(<<>>, 0, 0, R1, F@_2, F@_3,
+ TrUserData) ->
+ S1 = #{name => F@_2, repository => F@_3},
+ if R1 == '$undef' -> S1;
+ true -> S1#{releases => lists_reverse(R1, TrUserData)}
+ end;
+dfp_read_field_def_Package(Other, Z1, Z2, F@_1, F@_2,
+ F@_3, TrUserData) ->
+ dg_read_field_def_Package(Other, Z1, Z2, F@_1, F@_2,
+ F@_3, TrUserData).
+
+dg_read_field_def_Package(<<1:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, F@_3, TrUserData)
+ when N < 32 - 7 ->
+ dg_read_field_def_Package(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, F@_3, TrUserData);
+dg_read_field_def_Package(<<0:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, F@_3, TrUserData) ->
+ Key = X bsl N + Acc,
+ case Key of
+ 10 ->
+ d_field_Package_releases(Rest, 0, 0, F@_1, F@_2, F@_3,
+ TrUserData);
+ 18 ->
+ d_field_Package_name(Rest, 0, 0, F@_1, F@_2, F@_3,
+ TrUserData);
+ 26 ->
+ d_field_Package_repository(Rest, 0, 0, F@_1, F@_2, F@_3,
+ TrUserData);
+ _ ->
+ case Key band 7 of
+ 0 ->
+ skip_varint_Package(Rest, 0, 0, F@_1, F@_2, F@_3,
+ TrUserData);
+ 1 ->
+ skip_64_Package(Rest, 0, 0, F@_1, F@_2, F@_3,
+ TrUserData);
+ 2 ->
+ skip_length_delimited_Package(Rest, 0, 0, F@_1, F@_2,
+ F@_3, TrUserData);
+ 3 ->
+ skip_group_Package(Rest, Key bsr 3, 0, F@_1, F@_2, F@_3,
+ TrUserData);
+ 5 ->
+ skip_32_Package(Rest, 0, 0, F@_1, F@_2, F@_3,
+ TrUserData)
+ end
+ end;
+dg_read_field_def_Package(<<>>, 0, 0, R1, F@_2, F@_3,
+ TrUserData) ->
+ S1 = #{name => F@_2, repository => F@_3},
+ if R1 == '$undef' -> S1;
+ true -> S1#{releases => lists_reverse(R1, TrUserData)}
+ end.
+
+d_field_Package_releases(<<1:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, F@_3, TrUserData)
+ when N < 57 ->
+ d_field_Package_releases(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, F@_3, TrUserData);
+d_field_Package_releases(<<0:1, X:7, Rest/binary>>, N,
+ Acc, Prev, F@_2, F@_3, TrUserData) ->
+ {NewFValue, RestF} = begin
+ Len = X bsl N + Acc,
+ <<Bs:Len/binary, Rest2/binary>> = Rest,
+ {id(d_msg_Release(Bs, TrUserData), TrUserData),
+ Rest2}
+ end,
+ dfp_read_field_def_Package(RestF, 0, 0,
+ cons(NewFValue, Prev, TrUserData), F@_2, F@_3,
+ TrUserData).
+
+d_field_Package_name(<<1:1, X:7, Rest/binary>>, N, Acc,
+ F@_1, F@_2, F@_3, TrUserData)
+ when N < 57 ->
+ d_field_Package_name(Rest, N + 7, X bsl N + Acc, F@_1,
+ F@_2, F@_3, TrUserData);
+d_field_Package_name(<<0:1, X:7, Rest/binary>>, N, Acc,
+ F@_1, _, F@_3, TrUserData) ->
+ {NewFValue, RestF} = begin
+ Len = X bsl N + Acc,
+ <<Bytes:Len/binary, Rest2/binary>> = Rest,
+ {id(binary:copy(Bytes), TrUserData), Rest2}
+ end,
+ dfp_read_field_def_Package(RestF, 0, 0, F@_1, NewFValue,
+ F@_3, TrUserData).
+
+d_field_Package_repository(<<1:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, F@_3, TrUserData)
+ when N < 57 ->
+ d_field_Package_repository(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, F@_3, TrUserData);
+d_field_Package_repository(<<0:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, _, TrUserData) ->
+ {NewFValue, RestF} = begin
+ Len = X bsl N + Acc,
+ <<Bytes:Len/binary, Rest2/binary>> = Rest,
+ {id(binary:copy(Bytes), TrUserData), Rest2}
+ end,
+ dfp_read_field_def_Package(RestF, 0, 0, F@_1, F@_2,
+ NewFValue, TrUserData).
+
+skip_varint_Package(<<1:1, _:7, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, F@_3, TrUserData) ->
+ skip_varint_Package(Rest, Z1, Z2, F@_1, F@_2, F@_3,
+ TrUserData);
+skip_varint_Package(<<0:1, _:7, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, F@_3, TrUserData) ->
+ dfp_read_field_def_Package(Rest, Z1, Z2, F@_1, F@_2,
+ F@_3, TrUserData).
+
+skip_length_delimited_Package(<<1:1, X:7, Rest/binary>>,
+ N, Acc, F@_1, F@_2, F@_3, TrUserData)
+ when N < 57 ->
+ skip_length_delimited_Package(Rest, N + 7,
+ X bsl N + Acc, F@_1, F@_2, F@_3, TrUserData);
+skip_length_delimited_Package(<<0:1, X:7, Rest/binary>>,
+ N, Acc, F@_1, F@_2, F@_3, TrUserData) ->
+ Length = X bsl N + Acc,
+ <<_:Length/binary, Rest2/binary>> = Rest,
+ dfp_read_field_def_Package(Rest2, 0, 0, F@_1, F@_2,
+ F@_3, TrUserData).
+
+skip_group_Package(Bin, FNum, Z2, F@_1, F@_2, F@_3,
+ TrUserData) ->
+ {_, Rest} = read_group(Bin, FNum),
+ dfp_read_field_def_Package(Rest, 0, Z2, F@_1, F@_2,
+ F@_3, TrUserData).
+
+skip_32_Package(<<_:32, Rest/binary>>, Z1, Z2, F@_1,
+ F@_2, F@_3, TrUserData) ->
+ dfp_read_field_def_Package(Rest, Z1, Z2, F@_1, F@_2,
+ F@_3, TrUserData).
+
+skip_64_Package(<<_:64, Rest/binary>>, Z1, Z2, F@_1,
+ F@_2, F@_3, TrUserData) ->
+ dfp_read_field_def_Package(Rest, Z1, Z2, F@_1, F@_2,
+ F@_3, TrUserData).
+
+d_msg_Release(Bin, TrUserData) ->
+ dfp_read_field_def_Release(Bin, 0, 0,
+ id('$undef', TrUserData),
+ id('$undef', TrUserData), id([], TrUserData),
+ id('$undef', TrUserData), TrUserData).
+
+dfp_read_field_def_Release(<<10, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, F@_3, F@_4, TrUserData) ->
+ d_field_Release_version(Rest, Z1, Z2, F@_1, F@_2, F@_3,
+ F@_4, TrUserData);
+dfp_read_field_def_Release(<<18, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, F@_3, F@_4, TrUserData) ->
+ d_field_Release_checksum(Rest, Z1, Z2, F@_1, F@_2, F@_3,
+ F@_4, TrUserData);
+dfp_read_field_def_Release(<<26, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, F@_3, F@_4, TrUserData) ->
+ d_field_Release_dependencies(Rest, Z1, Z2, F@_1, F@_2,
+ F@_3, F@_4, TrUserData);
+dfp_read_field_def_Release(<<34, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, F@_3, F@_4, TrUserData) ->
+ d_field_Release_retired(Rest, Z1, Z2, F@_1, F@_2, F@_3,
+ F@_4, TrUserData);
+dfp_read_field_def_Release(<<>>, 0, 0, F@_1, F@_2, R1,
+ F@_4, TrUserData) ->
+ S1 = #{version => F@_1, checksum => F@_2},
+ S2 = if R1 == '$undef' -> S1;
+ true ->
+ S1#{dependencies => lists_reverse(R1, TrUserData)}
+ end,
+ if F@_4 == '$undef' -> S2;
+ true -> S2#{retired => F@_4}
+ end;
+dfp_read_field_def_Release(Other, Z1, Z2, F@_1, F@_2,
+ F@_3, F@_4, TrUserData) ->
+ dg_read_field_def_Release(Other, Z1, Z2, F@_1, F@_2,
+ F@_3, F@_4, TrUserData).
+
+dg_read_field_def_Release(<<1:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, F@_3, F@_4, TrUserData)
+ when N < 32 - 7 ->
+ dg_read_field_def_Release(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, F@_3, F@_4, TrUserData);
+dg_read_field_def_Release(<<0:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, F@_3, F@_4, TrUserData) ->
+ Key = X bsl N + Acc,
+ case Key of
+ 10 ->
+ d_field_Release_version(Rest, 0, 0, F@_1, F@_2, F@_3,
+ F@_4, TrUserData);
+ 18 ->
+ d_field_Release_checksum(Rest, 0, 0, F@_1, F@_2, F@_3,
+ F@_4, TrUserData);
+ 26 ->
+ d_field_Release_dependencies(Rest, 0, 0, F@_1, F@_2,
+ F@_3, F@_4, TrUserData);
+ 34 ->
+ d_field_Release_retired(Rest, 0, 0, F@_1, F@_2, F@_3,
+ F@_4, TrUserData);
+ _ ->
+ case Key band 7 of
+ 0 ->
+ skip_varint_Release(Rest, 0, 0, F@_1, F@_2, F@_3, F@_4,
+ TrUserData);
+ 1 ->
+ skip_64_Release(Rest, 0, 0, F@_1, F@_2, F@_3, F@_4,
+ TrUserData);
+ 2 ->
+ skip_length_delimited_Release(Rest, 0, 0, F@_1, F@_2,
+ F@_3, F@_4, TrUserData);
+ 3 ->
+ skip_group_Release(Rest, Key bsr 3, 0, F@_1, F@_2, F@_3,
+ F@_4, TrUserData);
+ 5 ->
+ skip_32_Release(Rest, 0, 0, F@_1, F@_2, F@_3, F@_4,
+ TrUserData)
+ end
+ end;
+dg_read_field_def_Release(<<>>, 0, 0, F@_1, F@_2, R1,
+ F@_4, TrUserData) ->
+ S1 = #{version => F@_1, checksum => F@_2},
+ S2 = if R1 == '$undef' -> S1;
+ true ->
+ S1#{dependencies => lists_reverse(R1, TrUserData)}
+ end,
+ if F@_4 == '$undef' -> S2;
+ true -> S2#{retired => F@_4}
+ end.
+
+d_field_Release_version(<<1:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, F@_3, F@_4, TrUserData)
+ when N < 57 ->
+ d_field_Release_version(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, F@_3, F@_4, TrUserData);
+d_field_Release_version(<<0:1, X:7, Rest/binary>>, N,
+ Acc, _, F@_2, F@_3, F@_4, TrUserData) ->
+ {NewFValue, RestF} = begin
+ Len = X bsl N + Acc,
+ <<Bytes:Len/binary, Rest2/binary>> = Rest,
+ {id(binary:copy(Bytes), TrUserData), Rest2}
+ end,
+ dfp_read_field_def_Release(RestF, 0, 0, NewFValue, F@_2,
+ F@_3, F@_4, TrUserData).
+
+d_field_Release_checksum(<<1:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, F@_3, F@_4, TrUserData)
+ when N < 57 ->
+ d_field_Release_checksum(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, F@_3, F@_4, TrUserData);
+d_field_Release_checksum(<<0:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, _, F@_3, F@_4, TrUserData) ->
+ {NewFValue, RestF} = begin
+ Len = X bsl N + Acc,
+ <<Bytes:Len/binary, Rest2/binary>> = Rest,
+ {id(binary:copy(Bytes), TrUserData), Rest2}
+ end,
+ dfp_read_field_def_Release(RestF, 0, 0, F@_1, NewFValue,
+ F@_3, F@_4, TrUserData).
+
+d_field_Release_dependencies(<<1:1, X:7, Rest/binary>>,
+ N, Acc, F@_1, F@_2, F@_3, F@_4, TrUserData)
+ when N < 57 ->
+ d_field_Release_dependencies(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, F@_3, F@_4, TrUserData);
+d_field_Release_dependencies(<<0:1, X:7, Rest/binary>>,
+ N, Acc, F@_1, F@_2, Prev, F@_4, TrUserData) ->
+ {NewFValue, RestF} = begin
+ Len = X bsl N + Acc,
+ <<Bs:Len/binary, Rest2/binary>> = Rest,
+ {id(d_msg_Dependency(Bs, TrUserData), TrUserData),
+ Rest2}
+ end,
+ dfp_read_field_def_Release(RestF, 0, 0, F@_1, F@_2,
+ cons(NewFValue, Prev, TrUserData), F@_4,
+ TrUserData).
+
+d_field_Release_retired(<<1:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, F@_3, F@_4, TrUserData)
+ when N < 57 ->
+ d_field_Release_retired(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, F@_3, F@_4, TrUserData);
+d_field_Release_retired(<<0:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, F@_3, Prev, TrUserData) ->
+ {NewFValue, RestF} = begin
+ Len = X bsl N + Acc,
+ <<Bs:Len/binary, Rest2/binary>> = Rest,
+ {id(d_msg_RetirementStatus(Bs, TrUserData),
+ TrUserData),
+ Rest2}
+ end,
+ dfp_read_field_def_Release(RestF, 0, 0, F@_1, F@_2,
+ F@_3,
+ if Prev == '$undef' -> NewFValue;
+ true ->
+ merge_msg_RetirementStatus(Prev,
+ NewFValue,
+ TrUserData)
+ end,
+ TrUserData).
+
+skip_varint_Release(<<1:1, _:7, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, F@_3, F@_4, TrUserData) ->
+ skip_varint_Release(Rest, Z1, Z2, F@_1, F@_2, F@_3,
+ F@_4, TrUserData);
+skip_varint_Release(<<0:1, _:7, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, F@_3, F@_4, TrUserData) ->
+ dfp_read_field_def_Release(Rest, Z1, Z2, F@_1, F@_2,
+ F@_3, F@_4, TrUserData).
+
+skip_length_delimited_Release(<<1:1, X:7, Rest/binary>>,
+ N, Acc, F@_1, F@_2, F@_3, F@_4, TrUserData)
+ when N < 57 ->
+ skip_length_delimited_Release(Rest, N + 7,
+ X bsl N + Acc, F@_1, F@_2, F@_3, F@_4,
+ TrUserData);
+skip_length_delimited_Release(<<0:1, X:7, Rest/binary>>,
+ N, Acc, F@_1, F@_2, F@_3, F@_4, TrUserData) ->
+ Length = X bsl N + Acc,
+ <<_:Length/binary, Rest2/binary>> = Rest,
+ dfp_read_field_def_Release(Rest2, 0, 0, F@_1, F@_2,
+ F@_3, F@_4, TrUserData).
+
+skip_group_Release(Bin, FNum, Z2, F@_1, F@_2, F@_3,
+ F@_4, TrUserData) ->
+ {_, Rest} = read_group(Bin, FNum),
+ dfp_read_field_def_Release(Rest, 0, Z2, F@_1, F@_2,
+ F@_3, F@_4, TrUserData).
+
+skip_32_Release(<<_:32, Rest/binary>>, Z1, Z2, F@_1,
+ F@_2, F@_3, F@_4, TrUserData) ->
+ dfp_read_field_def_Release(Rest, Z1, Z2, F@_1, F@_2,
+ F@_3, F@_4, TrUserData).
+
+skip_64_Release(<<_:64, Rest/binary>>, Z1, Z2, F@_1,
+ F@_2, F@_3, F@_4, TrUserData) ->
+ dfp_read_field_def_Release(Rest, Z1, Z2, F@_1, F@_2,
+ F@_3, F@_4, TrUserData).
+
+d_msg_RetirementStatus(Bin, TrUserData) ->
+ dfp_read_field_def_RetirementStatus(Bin, 0, 0,
+ id('$undef', TrUserData),
+ id('$undef', TrUserData), TrUserData).
+
+dfp_read_field_def_RetirementStatus(<<8, Rest/binary>>,
+ Z1, Z2, F@_1, F@_2, TrUserData) ->
+ d_field_RetirementStatus_reason(Rest, Z1, Z2, F@_1,
+ F@_2, TrUserData);
+dfp_read_field_def_RetirementStatus(<<18, Rest/binary>>,
+ Z1, Z2, F@_1, F@_2, TrUserData) ->
+ d_field_RetirementStatus_message(Rest, Z1, Z2, F@_1,
+ F@_2, TrUserData);
+dfp_read_field_def_RetirementStatus(<<>>, 0, 0, F@_1,
+ F@_2, _) ->
+ S1 = #{reason => F@_1},
+ if F@_2 == '$undef' -> S1;
+ true -> S1#{message => F@_2}
+ end;
+dfp_read_field_def_RetirementStatus(Other, Z1, Z2, F@_1,
+ F@_2, TrUserData) ->
+ dg_read_field_def_RetirementStatus(Other, Z1, Z2, F@_1,
+ F@_2, TrUserData).
+
+dg_read_field_def_RetirementStatus(<<1:1, X:7,
+ Rest/binary>>,
+ N, Acc, F@_1, F@_2, TrUserData)
+ when N < 32 - 7 ->
+ dg_read_field_def_RetirementStatus(Rest, N + 7,
+ X bsl N + Acc, F@_1, F@_2, TrUserData);
+dg_read_field_def_RetirementStatus(<<0:1, X:7,
+ Rest/binary>>,
+ N, Acc, F@_1, F@_2, TrUserData) ->
+ Key = X bsl N + Acc,
+ case Key of
+ 8 ->
+ d_field_RetirementStatus_reason(Rest, 0, 0, F@_1, F@_2,
+ TrUserData);
+ 18 ->
+ d_field_RetirementStatus_message(Rest, 0, 0, F@_1, F@_2,
+ TrUserData);
+ _ ->
+ case Key band 7 of
+ 0 ->
+ skip_varint_RetirementStatus(Rest, 0, 0, F@_1, F@_2,
+ TrUserData);
+ 1 ->
+ skip_64_RetirementStatus(Rest, 0, 0, F@_1, F@_2,
+ TrUserData);
+ 2 ->
+ skip_length_delimited_RetirementStatus(Rest, 0, 0, F@_1,
+ F@_2, TrUserData);
+ 3 ->
+ skip_group_RetirementStatus(Rest, Key bsr 3, 0, F@_1,
+ F@_2, TrUserData);
+ 5 ->
+ skip_32_RetirementStatus(Rest, 0, 0, F@_1, F@_2,
+ TrUserData)
+ end
+ end;
+dg_read_field_def_RetirementStatus(<<>>, 0, 0, F@_1,
+ F@_2, _) ->
+ S1 = #{reason => F@_1},
+ if F@_2 == '$undef' -> S1;
+ true -> S1#{message => F@_2}
+ end.
+
+d_field_RetirementStatus_reason(<<1:1, X:7,
+ Rest/binary>>,
+ N, Acc, F@_1, F@_2, TrUserData)
+ when N < 57 ->
+ d_field_RetirementStatus_reason(Rest, N + 7,
+ X bsl N + Acc, F@_1, F@_2, TrUserData);
+d_field_RetirementStatus_reason(<<0:1, X:7,
+ Rest/binary>>,
+ N, Acc, _, F@_2, TrUserData) ->
+ {NewFValue, RestF} = {id(d_enum_RetirementReason(begin
+ <<Res:32/signed-native>> =
+ <<(X bsl N +
+ Acc):32/unsigned-native>>,
+ id(Res, TrUserData)
+ end),
+ TrUserData),
+ Rest},
+ dfp_read_field_def_RetirementStatus(RestF, 0, 0,
+ NewFValue, F@_2, TrUserData).
+
+d_field_RetirementStatus_message(<<1:1, X:7,
+ Rest/binary>>,
+ N, Acc, F@_1, F@_2, TrUserData)
+ when N < 57 ->
+ d_field_RetirementStatus_message(Rest, N + 7,
+ X bsl N + Acc, F@_1, F@_2, TrUserData);
+d_field_RetirementStatus_message(<<0:1, X:7,
+ Rest/binary>>,
+ N, Acc, F@_1, _, TrUserData) ->
+ {NewFValue, RestF} = begin
+ Len = X bsl N + Acc,
+ <<Bytes:Len/binary, Rest2/binary>> = Rest,
+ {id(binary:copy(Bytes), TrUserData), Rest2}
+ end,
+ dfp_read_field_def_RetirementStatus(RestF, 0, 0, F@_1,
+ NewFValue, TrUserData).
+
+skip_varint_RetirementStatus(<<1:1, _:7, Rest/binary>>,
+ Z1, Z2, F@_1, F@_2, TrUserData) ->
+ skip_varint_RetirementStatus(Rest, Z1, Z2, F@_1, F@_2,
+ TrUserData);
+skip_varint_RetirementStatus(<<0:1, _:7, Rest/binary>>,
+ Z1, Z2, F@_1, F@_2, TrUserData) ->
+ dfp_read_field_def_RetirementStatus(Rest, Z1, Z2, F@_1,
+ F@_2, TrUserData).
+
+skip_length_delimited_RetirementStatus(<<1:1, X:7,
+ Rest/binary>>,
+ N, Acc, F@_1, F@_2, TrUserData)
+ when N < 57 ->
+ skip_length_delimited_RetirementStatus(Rest, N + 7,
+ X bsl N + Acc, F@_1, F@_2,
+ TrUserData);
+skip_length_delimited_RetirementStatus(<<0:1, X:7,
+ Rest/binary>>,
+ N, Acc, F@_1, F@_2, TrUserData) ->
+ Length = X bsl N + Acc,
+ <<_:Length/binary, Rest2/binary>> = Rest,
+ dfp_read_field_def_RetirementStatus(Rest2, 0, 0, F@_1,
+ F@_2, TrUserData).
+
+skip_group_RetirementStatus(Bin, FNum, Z2, F@_1, F@_2,
+ TrUserData) ->
+ {_, Rest} = read_group(Bin, FNum),
+ dfp_read_field_def_RetirementStatus(Rest, 0, Z2, F@_1,
+ F@_2, TrUserData).
+
+skip_32_RetirementStatus(<<_:32, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, TrUserData) ->
+ dfp_read_field_def_RetirementStatus(Rest, Z1, Z2, F@_1,
+ F@_2, TrUserData).
+
+skip_64_RetirementStatus(<<_:64, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, TrUserData) ->
+ dfp_read_field_def_RetirementStatus(Rest, Z1, Z2, F@_1,
+ F@_2, TrUserData).
+
+d_msg_Dependency(Bin, TrUserData) ->
+ dfp_read_field_def_Dependency(Bin, 0, 0,
+ id('$undef', TrUserData),
+ id('$undef', TrUserData),
+ id('$undef', TrUserData),
+ id('$undef', TrUserData),
+ id('$undef', TrUserData), TrUserData).
+
+dfp_read_field_def_Dependency(<<10, Rest/binary>>, Z1,
+ Z2, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) ->
+ d_field_Dependency_package(Rest, Z1, Z2, F@_1, F@_2,
+ F@_3, F@_4, F@_5, TrUserData);
+dfp_read_field_def_Dependency(<<18, Rest/binary>>, Z1,
+ Z2, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) ->
+ d_field_Dependency_requirement(Rest, Z1, Z2, F@_1, F@_2,
+ F@_3, F@_4, F@_5, TrUserData);
+dfp_read_field_def_Dependency(<<24, Rest/binary>>, Z1,
+ Z2, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) ->
+ d_field_Dependency_optional(Rest, Z1, Z2, F@_1, F@_2,
+ F@_3, F@_4, F@_5, TrUserData);
+dfp_read_field_def_Dependency(<<34, Rest/binary>>, Z1,
+ Z2, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) ->
+ d_field_Dependency_app(Rest, Z1, Z2, F@_1, F@_2, F@_3,
+ F@_4, F@_5, TrUserData);
+dfp_read_field_def_Dependency(<<42, Rest/binary>>, Z1,
+ Z2, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) ->
+ d_field_Dependency_repository(Rest, Z1, Z2, F@_1, F@_2,
+ F@_3, F@_4, F@_5, TrUserData);
+dfp_read_field_def_Dependency(<<>>, 0, 0, F@_1, F@_2,
+ F@_3, F@_4, F@_5, _) ->
+ S1 = #{package => F@_1, requirement => F@_2},
+ S2 = if F@_3 == '$undef' -> S1;
+ true -> S1#{optional => F@_3}
+ end,
+ S3 = if F@_4 == '$undef' -> S2;
+ true -> S2#{app => F@_4}
+ end,
+ if F@_5 == '$undef' -> S3;
+ true -> S3#{repository => F@_5}
+ end;
+dfp_read_field_def_Dependency(Other, Z1, Z2, F@_1, F@_2,
+ F@_3, F@_4, F@_5, TrUserData) ->
+ dg_read_field_def_Dependency(Other, Z1, Z2, F@_1, F@_2,
+ F@_3, F@_4, F@_5, TrUserData).
+
+dg_read_field_def_Dependency(<<1:1, X:7, Rest/binary>>,
+ N, Acc, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData)
+ when N < 32 - 7 ->
+ dg_read_field_def_Dependency(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData);
+dg_read_field_def_Dependency(<<0:1, X:7, Rest/binary>>,
+ N, Acc, F@_1, F@_2, F@_3, F@_4, F@_5,
+ TrUserData) ->
+ Key = X bsl N + Acc,
+ case Key of
+ 10 ->
+ d_field_Dependency_package(Rest, 0, 0, F@_1, F@_2, F@_3,
+ F@_4, F@_5, TrUserData);
+ 18 ->
+ d_field_Dependency_requirement(Rest, 0, 0, F@_1, F@_2,
+ F@_3, F@_4, F@_5, TrUserData);
+ 24 ->
+ d_field_Dependency_optional(Rest, 0, 0, F@_1, F@_2,
+ F@_3, F@_4, F@_5, TrUserData);
+ 34 ->
+ d_field_Dependency_app(Rest, 0, 0, F@_1, F@_2, F@_3,
+ F@_4, F@_5, TrUserData);
+ 42 ->
+ d_field_Dependency_repository(Rest, 0, 0, F@_1, F@_2,
+ F@_3, F@_4, F@_5, TrUserData);
+ _ ->
+ case Key band 7 of
+ 0 ->
+ skip_varint_Dependency(Rest, 0, 0, F@_1, F@_2, F@_3,
+ F@_4, F@_5, TrUserData);
+ 1 ->
+ skip_64_Dependency(Rest, 0, 0, F@_1, F@_2, F@_3, F@_4,
+ F@_5, TrUserData);
+ 2 ->
+ skip_length_delimited_Dependency(Rest, 0, 0, F@_1, F@_2,
+ F@_3, F@_4, F@_5, TrUserData);
+ 3 ->
+ skip_group_Dependency(Rest, Key bsr 3, 0, F@_1, F@_2,
+ F@_3, F@_4, F@_5, TrUserData);
+ 5 ->
+ skip_32_Dependency(Rest, 0, 0, F@_1, F@_2, F@_3, F@_4,
+ F@_5, TrUserData)
+ end
+ end;
+dg_read_field_def_Dependency(<<>>, 0, 0, F@_1, F@_2,
+ F@_3, F@_4, F@_5, _) ->
+ S1 = #{package => F@_1, requirement => F@_2},
+ S2 = if F@_3 == '$undef' -> S1;
+ true -> S1#{optional => F@_3}
+ end,
+ S3 = if F@_4 == '$undef' -> S2;
+ true -> S2#{app => F@_4}
+ end,
+ if F@_5 == '$undef' -> S3;
+ true -> S3#{repository => F@_5}
+ end.
+
+d_field_Dependency_package(<<1:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData)
+ when N < 57 ->
+ d_field_Dependency_package(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData);
+d_field_Dependency_package(<<0:1, X:7, Rest/binary>>, N,
+ Acc, _, F@_2, F@_3, F@_4, F@_5, TrUserData) ->
+ {NewFValue, RestF} = begin
+ Len = X bsl N + Acc,
+ <<Bytes:Len/binary, Rest2/binary>> = Rest,
+ {id(binary:copy(Bytes), TrUserData), Rest2}
+ end,
+ dfp_read_field_def_Dependency(RestF, 0, 0, NewFValue,
+ F@_2, F@_3, F@_4, F@_5, TrUserData).
+
+d_field_Dependency_requirement(<<1:1, X:7,
+ Rest/binary>>,
+ N, Acc, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData)
+ when N < 57 ->
+ d_field_Dependency_requirement(Rest, N + 7,
+ X bsl N + Acc, F@_1, F@_2, F@_3, F@_4, F@_5,
+ TrUserData);
+d_field_Dependency_requirement(<<0:1, X:7,
+ Rest/binary>>,
+ N, Acc, F@_1, _, F@_3, F@_4, F@_5, TrUserData) ->
+ {NewFValue, RestF} = begin
+ Len = X bsl N + Acc,
+ <<Bytes:Len/binary, Rest2/binary>> = Rest,
+ {id(binary:copy(Bytes), TrUserData), Rest2}
+ end,
+ dfp_read_field_def_Dependency(RestF, 0, 0, F@_1,
+ NewFValue, F@_3, F@_4, F@_5, TrUserData).
+
+d_field_Dependency_optional(<<1:1, X:7, Rest/binary>>,
+ N, Acc, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData)
+ when N < 57 ->
+ d_field_Dependency_optional(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData);
+d_field_Dependency_optional(<<0:1, X:7, Rest/binary>>,
+ N, Acc, F@_1, F@_2, _, F@_4, F@_5, TrUserData) ->
+ {NewFValue, RestF} = {id(X bsl N + Acc =/= 0,
+ TrUserData),
+ Rest},
+ dfp_read_field_def_Dependency(RestF, 0, 0, F@_1, F@_2,
+ NewFValue, F@_4, F@_5, TrUserData).
+
+d_field_Dependency_app(<<1:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData)
+ when N < 57 ->
+ d_field_Dependency_app(Rest, N + 7, X bsl N + Acc, F@_1,
+ F@_2, F@_3, F@_4, F@_5, TrUserData);
+d_field_Dependency_app(<<0:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, F@_3, _, F@_5, TrUserData) ->
+ {NewFValue, RestF} = begin
+ Len = X bsl N + Acc,
+ <<Bytes:Len/binary, Rest2/binary>> = Rest,
+ {id(binary:copy(Bytes), TrUserData), Rest2}
+ end,
+ dfp_read_field_def_Dependency(RestF, 0, 0, F@_1, F@_2,
+ F@_3, NewFValue, F@_5, TrUserData).
+
+d_field_Dependency_repository(<<1:1, X:7, Rest/binary>>,
+ N, Acc, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData)
+ when N < 57 ->
+ d_field_Dependency_repository(Rest, N + 7,
+ X bsl N + Acc, F@_1, F@_2, F@_3, F@_4, F@_5,
+ TrUserData);
+d_field_Dependency_repository(<<0:1, X:7, Rest/binary>>,
+ N, Acc, F@_1, F@_2, F@_3, F@_4, _, TrUserData) ->
+ {NewFValue, RestF} = begin
+ Len = X bsl N + Acc,
+ <<Bytes:Len/binary, Rest2/binary>> = Rest,
+ {id(binary:copy(Bytes), TrUserData), Rest2}
+ end,
+ dfp_read_field_def_Dependency(RestF, 0, 0, F@_1, F@_2,
+ F@_3, F@_4, NewFValue, TrUserData).
+
+skip_varint_Dependency(<<1:1, _:7, Rest/binary>>, Z1,
+ Z2, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) ->
+ skip_varint_Dependency(Rest, Z1, Z2, F@_1, F@_2, F@_3,
+ F@_4, F@_5, TrUserData);
+skip_varint_Dependency(<<0:1, _:7, Rest/binary>>, Z1,
+ Z2, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) ->
+ dfp_read_field_def_Dependency(Rest, Z1, Z2, F@_1, F@_2,
+ F@_3, F@_4, F@_5, TrUserData).
+
+skip_length_delimited_Dependency(<<1:1, X:7,
+ Rest/binary>>,
+ N, Acc, F@_1, F@_2, F@_3, F@_4, F@_5,
+ TrUserData)
+ when N < 57 ->
+ skip_length_delimited_Dependency(Rest, N + 7,
+ X bsl N + Acc, F@_1, F@_2, F@_3, F@_4,
+ F@_5, TrUserData);
+skip_length_delimited_Dependency(<<0:1, X:7,
+ Rest/binary>>,
+ N, Acc, F@_1, F@_2, F@_3, F@_4, F@_5,
+ TrUserData) ->
+ Length = X bsl N + Acc,
+ <<_:Length/binary, Rest2/binary>> = Rest,
+ dfp_read_field_def_Dependency(Rest2, 0, 0, F@_1, F@_2,
+ F@_3, F@_4, F@_5, TrUserData).
+
+skip_group_Dependency(Bin, FNum, Z2, F@_1, F@_2, F@_3,
+ F@_4, F@_5, TrUserData) ->
+ {_, Rest} = read_group(Bin, FNum),
+ dfp_read_field_def_Dependency(Rest, 0, Z2, F@_1, F@_2,
+ F@_3, F@_4, F@_5, TrUserData).
+
+skip_32_Dependency(<<_:32, Rest/binary>>, Z1, Z2, F@_1,
+ F@_2, F@_3, F@_4, F@_5, TrUserData) ->
+ dfp_read_field_def_Dependency(Rest, Z1, Z2, F@_1, F@_2,
+ F@_3, F@_4, F@_5, TrUserData).
+
+skip_64_Dependency(<<_:64, Rest/binary>>, Z1, Z2, F@_1,
+ F@_2, F@_3, F@_4, F@_5, TrUserData) ->
+ dfp_read_field_def_Dependency(Rest, Z1, Z2, F@_1, F@_2,
+ F@_3, F@_4, F@_5, TrUserData).
+
+d_enum_RetirementReason(0) -> 'RETIRED_OTHER';
+d_enum_RetirementReason(1) -> 'RETIRED_INVALID';
+d_enum_RetirementReason(2) -> 'RETIRED_SECURITY';
+d_enum_RetirementReason(3) -> 'RETIRED_DEPRECATED';
+d_enum_RetirementReason(4) -> 'RETIRED_RENAMED';
+d_enum_RetirementReason(V) -> V.
+
+read_group(Bin, FieldNum) ->
+ {NumBytes, EndTagLen} = read_gr_b(Bin, 0, 0, 0, 0, FieldNum),
+ <<Group:NumBytes/binary, _:EndTagLen/binary, Rest/binary>> = Bin,
+ {Group, Rest}.
+
+%% Like skipping over fields, but record the total length,
+%% Each field is <(FieldNum bsl 3) bor FieldType> ++ <FieldValue>
+%% Record the length because varints may be non-optimally encoded.
+%%
+%% Groups can be nested, but assume the same FieldNum cannot be nested
+%% because group field numbers are shared with the rest of the fields
+%% numbers. Thus we can search just for an group-end with the same
+%% field number.
+%%
+%% (The only time the same group field number could occur would
+%% be in a nested sub message, but then it would be inside a
+%% length-delimited entry, which we skip-read by length.)
+read_gr_b(<<1:1, X:7, Tl/binary>>, N, Acc, NumBytes, TagLen, FieldNum)
+ when N < (32-7) ->
+ read_gr_b(Tl, N+7, X bsl N + Acc, NumBytes, TagLen+1, FieldNum);
+read_gr_b(<<0:1, X:7, Tl/binary>>, N, Acc, NumBytes, TagLen,
+ FieldNum) ->
+ Key = X bsl N + Acc,
+ TagLen1 = TagLen + 1,
+ case {Key bsr 3, Key band 7} of
+ {FieldNum, 4} -> % 4 = group_end
+ {NumBytes, TagLen1};
+ {_, 0} -> % 0 = varint
+ read_gr_vi(Tl, 0, NumBytes + TagLen1, FieldNum);
+ {_, 1} -> % 1 = bits64
+ <<_:64, Tl2/binary>> = Tl,
+ read_gr_b(Tl2, 0, 0, NumBytes + TagLen1 + 8, 0, FieldNum);
+ {_, 2} -> % 2 = length_delimited
+ read_gr_ld(Tl, 0, 0, NumBytes + TagLen1, FieldNum);
+ {_, 3} -> % 3 = group_start
+ read_gr_b(Tl, 0, 0, NumBytes + TagLen1, 0, FieldNum);
+ {_, 4} -> % 4 = group_end
+ read_gr_b(Tl, 0, 0, NumBytes + TagLen1, 0, FieldNum);
+ {_, 5} -> % 5 = bits32
+ <<_:32, Tl2/binary>> = Tl,
+ read_gr_b(Tl2, 0, 0, NumBytes + TagLen1 + 4, 0, FieldNum)
+ end.
+
+read_gr_vi(<<1:1, _:7, Tl/binary>>, N, NumBytes, FieldNum)
+ when N < (64-7) ->
+ read_gr_vi(Tl, N+7, NumBytes+1, FieldNum);
+read_gr_vi(<<0:1, _:7, Tl/binary>>, _, NumBytes, FieldNum) ->
+ read_gr_b(Tl, 0, 0, NumBytes+1, 0, FieldNum).
+
+read_gr_ld(<<1:1, X:7, Tl/binary>>, N, Acc, NumBytes, FieldNum)
+ when N < (64-7) ->
+ read_gr_ld(Tl, N+7, X bsl N + Acc, NumBytes+1, FieldNum);
+read_gr_ld(<<0:1, X:7, Tl/binary>>, N, Acc, NumBytes, FieldNum) ->
+ Len = X bsl N + Acc,
+ NumBytes1 = NumBytes + 1,
+ <<_:Len/binary, Tl2/binary>> = Tl,
+ read_gr_b(Tl2, 0, 0, NumBytes1 + Len, 0, FieldNum).
+
+merge_msgs(Prev, New, MsgName) when is_atom(MsgName) ->
+ merge_msgs(Prev, New, MsgName, []).
+
+merge_msgs(Prev, New, MsgName, Opts) ->
+ TrUserData = proplists:get_value(user_data, Opts),
+ case MsgName of
+ 'Package' -> merge_msg_Package(Prev, New, TrUserData);
+ 'Release' -> merge_msg_Release(Prev, New, TrUserData);
+ 'RetirementStatus' ->
+ merge_msg_RetirementStatus(Prev, New, TrUserData);
+ 'Dependency' ->
+ merge_msg_Dependency(Prev, New, TrUserData)
+ end.
+
+-compile({nowarn_unused_function,merge_msg_Package/3}).
+merge_msg_Package(#{} = PMsg,
+ #{name := NFname, repository := NFrepository} = NMsg,
+ TrUserData) ->
+ S1 = #{name => NFname, repository => NFrepository},
+ case {PMsg, NMsg} of
+ {#{releases := PFreleases},
+ #{releases := NFreleases}} ->
+ S1#{releases =>
+ 'erlang_++'(PFreleases, NFreleases, TrUserData)};
+ {_, #{releases := NFreleases}} ->
+ S1#{releases => NFreleases};
+ {#{releases := PFreleases}, _} ->
+ S1#{releases => PFreleases};
+ {_, _} -> S1
+ end.
+
+-compile({nowarn_unused_function,merge_msg_Release/3}).
+merge_msg_Release(#{} = PMsg,
+ #{version := NFversion, checksum := NFchecksum} = NMsg,
+ TrUserData) ->
+ S1 = #{version => NFversion, checksum => NFchecksum},
+ S2 = case {PMsg, NMsg} of
+ {#{dependencies := PFdependencies},
+ #{dependencies := NFdependencies}} ->
+ S1#{dependencies =>
+ 'erlang_++'(PFdependencies, NFdependencies,
+ TrUserData)};
+ {_, #{dependencies := NFdependencies}} ->
+ S1#{dependencies => NFdependencies};
+ {#{dependencies := PFdependencies}, _} ->
+ S1#{dependencies => PFdependencies};
+ {_, _} -> S1
+ end,
+ case {PMsg, NMsg} of
+ {#{retired := PFretired}, #{retired := NFretired}} ->
+ S2#{retired =>
+ merge_msg_RetirementStatus(PFretired, NFretired,
+ TrUserData)};
+ {_, #{retired := NFretired}} ->
+ S2#{retired => NFretired};
+ {#{retired := PFretired}, _} ->
+ S2#{retired => PFretired};
+ {_, _} -> S2
+ end.
+
+-compile({nowarn_unused_function,merge_msg_RetirementStatus/3}).
+merge_msg_RetirementStatus(#{} = PMsg,
+ #{reason := NFreason} = NMsg, _) ->
+ S1 = #{reason => NFreason},
+ case {PMsg, NMsg} of
+ {_, #{message := NFmessage}} ->
+ S1#{message => NFmessage};
+ {#{message := PFmessage}, _} ->
+ S1#{message => PFmessage};
+ _ -> S1
+ end.
+
+-compile({nowarn_unused_function,merge_msg_Dependency/3}).
+merge_msg_Dependency(#{} = PMsg,
+ #{package := NFpackage, requirement := NFrequirement} =
+ NMsg,
+ _) ->
+ S1 = #{package => NFpackage,
+ requirement => NFrequirement},
+ S2 = case {PMsg, NMsg} of
+ {_, #{optional := NFoptional}} ->
+ S1#{optional => NFoptional};
+ {#{optional := PFoptional}, _} ->
+ S1#{optional => PFoptional};
+ _ -> S1
+ end,
+ S3 = case {PMsg, NMsg} of
+ {_, #{app := NFapp}} -> S2#{app => NFapp};
+ {#{app := PFapp}, _} -> S2#{app => PFapp};
+ _ -> S2
+ end,
+ case {PMsg, NMsg} of
+ {_, #{repository := NFrepository}} ->
+ S3#{repository => NFrepository};
+ {#{repository := PFrepository}, _} ->
+ S3#{repository => PFrepository};
+ _ -> S3
+ end.
+
+
+verify_msg(Msg, MsgName) when is_atom(MsgName) ->
+ verify_msg(Msg, MsgName, []).
+
+verify_msg(Msg, MsgName, Opts) ->
+ TrUserData = proplists:get_value(user_data, Opts),
+ case MsgName of
+ 'Package' -> v_msg_Package(Msg, [MsgName], TrUserData);
+ 'Release' -> v_msg_Release(Msg, [MsgName], TrUserData);
+ 'RetirementStatus' ->
+ v_msg_RetirementStatus(Msg, [MsgName], TrUserData);
+ 'Dependency' ->
+ v_msg_Dependency(Msg, [MsgName], TrUserData);
+ _ -> mk_type_error(not_a_known_message, Msg, [])
+ end.
+
+
+-compile({nowarn_unused_function,v_msg_Package/3}).
+v_msg_Package(#{name := F2, repository := F3} = M, Path,
+ TrUserData) ->
+ case M of
+ #{releases := F1} ->
+ if is_list(F1) ->
+ _ = [v_msg_Release(Elem, [releases | Path], TrUserData)
+ || Elem <- F1],
+ ok;
+ true ->
+ mk_type_error({invalid_list_of, {msg, 'Release'}}, F1,
+ [releases | Path])
+ end;
+ _ -> ok
+ end,
+ v_type_string(F2, [name | Path], TrUserData),
+ v_type_string(F3, [repository | Path], TrUserData),
+ lists:foreach(fun (releases) -> ok;
+ (name) -> ok;
+ (repository) -> ok;
+ (OtherKey) ->
+ mk_type_error({extraneous_key, OtherKey}, M, Path)
+ end,
+ maps:keys(M)),
+ ok;
+v_msg_Package(M, Path, _TrUserData) when is_map(M) ->
+ mk_type_error({missing_fields,
+ [name, repository] -- maps:keys(M), 'Package'},
+ M, Path);
+v_msg_Package(X, Path, _TrUserData) ->
+ mk_type_error({expected_msg, 'Package'}, X, Path).
+
+-compile({nowarn_unused_function,v_msg_Release/3}).
+v_msg_Release(#{version := F1, checksum := F2} = M,
+ Path, TrUserData) ->
+ v_type_string(F1, [version | Path], TrUserData),
+ v_type_bytes(F2, [checksum | Path], TrUserData),
+ case M of
+ #{dependencies := F3} ->
+ if is_list(F3) ->
+ _ = [v_msg_Dependency(Elem, [dependencies | Path],
+ TrUserData)
+ || Elem <- F3],
+ ok;
+ true ->
+ mk_type_error({invalid_list_of, {msg, 'Dependency'}},
+ F3, [dependencies | Path])
+ end;
+ _ -> ok
+ end,
+ case M of
+ #{retired := F4} ->
+ v_msg_RetirementStatus(F4, [retired | Path],
+ TrUserData);
+ _ -> ok
+ end,
+ lists:foreach(fun (version) -> ok;
+ (checksum) -> ok;
+ (dependencies) -> ok;
+ (retired) -> ok;
+ (OtherKey) ->
+ mk_type_error({extraneous_key, OtherKey}, M, Path)
+ end,
+ maps:keys(M)),
+ ok;
+v_msg_Release(M, Path, _TrUserData) when is_map(M) ->
+ mk_type_error({missing_fields,
+ [version, checksum] -- maps:keys(M), 'Release'},
+ M, Path);
+v_msg_Release(X, Path, _TrUserData) ->
+ mk_type_error({expected_msg, 'Release'}, X, Path).
+
+-compile({nowarn_unused_function,v_msg_RetirementStatus/3}).
+v_msg_RetirementStatus(#{reason := F1} = M, Path,
+ TrUserData) ->
+ v_enum_RetirementReason(F1, [reason | Path],
+ TrUserData),
+ case M of
+ #{message := F2} ->
+ v_type_string(F2, [message | Path], TrUserData);
+ _ -> ok
+ end,
+ lists:foreach(fun (reason) -> ok;
+ (message) -> ok;
+ (OtherKey) ->
+ mk_type_error({extraneous_key, OtherKey}, M, Path)
+ end,
+ maps:keys(M)),
+ ok;
+v_msg_RetirementStatus(M, Path, _TrUserData)
+ when is_map(M) ->
+ mk_type_error({missing_fields, [reason] -- maps:keys(M),
+ 'RetirementStatus'},
+ M, Path);
+v_msg_RetirementStatus(X, Path, _TrUserData) ->
+ mk_type_error({expected_msg, 'RetirementStatus'}, X,
+ Path).
+
+-compile({nowarn_unused_function,v_msg_Dependency/3}).
+v_msg_Dependency(#{package := F1, requirement := F2} =
+ M,
+ Path, TrUserData) ->
+ v_type_string(F1, [package | Path], TrUserData),
+ v_type_string(F2, [requirement | Path], TrUserData),
+ case M of
+ #{optional := F3} ->
+ v_type_bool(F3, [optional | Path], TrUserData);
+ _ -> ok
+ end,
+ case M of
+ #{app := F4} ->
+ v_type_string(F4, [app | Path], TrUserData);
+ _ -> ok
+ end,
+ case M of
+ #{repository := F5} ->
+ v_type_string(F5, [repository | Path], TrUserData);
+ _ -> ok
+ end,
+ lists:foreach(fun (package) -> ok;
+ (requirement) -> ok;
+ (optional) -> ok;
+ (app) -> ok;
+ (repository) -> ok;
+ (OtherKey) ->
+ mk_type_error({extraneous_key, OtherKey}, M, Path)
+ end,
+ maps:keys(M)),
+ ok;
+v_msg_Dependency(M, Path, _TrUserData) when is_map(M) ->
+ mk_type_error({missing_fields,
+ [package, requirement] -- maps:keys(M), 'Dependency'},
+ M, Path);
+v_msg_Dependency(X, Path, _TrUserData) ->
+ mk_type_error({expected_msg, 'Dependency'}, X, Path).
+
+-compile({nowarn_unused_function,v_enum_RetirementReason/3}).
+v_enum_RetirementReason('RETIRED_OTHER', _Path,
+ _TrUserData) ->
+ ok;
+v_enum_RetirementReason('RETIRED_INVALID', _Path,
+ _TrUserData) ->
+ ok;
+v_enum_RetirementReason('RETIRED_SECURITY', _Path,
+ _TrUserData) ->
+ ok;
+v_enum_RetirementReason('RETIRED_DEPRECATED', _Path,
+ _TrUserData) ->
+ ok;
+v_enum_RetirementReason('RETIRED_RENAMED', _Path,
+ _TrUserData) ->
+ ok;
+v_enum_RetirementReason(V, Path, TrUserData)
+ when is_integer(V) ->
+ v_type_sint32(V, Path, TrUserData);
+v_enum_RetirementReason(X, Path, _TrUserData) ->
+ mk_type_error({invalid_enum, 'RetirementReason'}, X,
+ Path).
+
+-compile({nowarn_unused_function,v_type_sint32/3}).
+v_type_sint32(N, _Path, _TrUserData)
+ when -2147483648 =< N, N =< 2147483647 ->
+ ok;
+v_type_sint32(N, Path, _TrUserData)
+ when is_integer(N) ->
+ mk_type_error({value_out_of_range, sint32, signed, 32},
+ N, Path);
+v_type_sint32(X, Path, _TrUserData) ->
+ mk_type_error({bad_integer, sint32, signed, 32}, X,
+ Path).
+
+-compile({nowarn_unused_function,v_type_bool/3}).
+v_type_bool(false, _Path, _TrUserData) -> ok;
+v_type_bool(true, _Path, _TrUserData) -> ok;
+v_type_bool(0, _Path, _TrUserData) -> ok;
+v_type_bool(1, _Path, _TrUserData) -> ok;
+v_type_bool(X, Path, _TrUserData) ->
+ mk_type_error(bad_boolean_value, X, Path).
+
+-compile({nowarn_unused_function,v_type_string/3}).
+v_type_string(S, Path, _TrUserData)
+ when is_list(S); is_binary(S) ->
+ try unicode:characters_to_binary(S) of
+ B when is_binary(B) -> ok;
+ {error, _, _} ->
+ mk_type_error(bad_unicode_string, S, Path)
+ catch
+ error:badarg ->
+ mk_type_error(bad_unicode_string, S, Path)
+ end;
+v_type_string(X, Path, _TrUserData) ->
+ mk_type_error(bad_unicode_string, X, Path).
+
+-compile({nowarn_unused_function,v_type_bytes/3}).
+v_type_bytes(B, _Path, _TrUserData) when is_binary(B) ->
+ ok;
+v_type_bytes(B, _Path, _TrUserData) when is_list(B) ->
+ ok;
+v_type_bytes(X, Path, _TrUserData) ->
+ mk_type_error(bad_binary_value, X, Path).
+
+-compile({nowarn_unused_function,mk_type_error/3}).
+-spec mk_type_error(_, _, list()) -> no_return().
+mk_type_error(Error, ValueSeen, Path) ->
+ Path2 = prettify_path(Path),
+ erlang:error({gpb_type_error,
+ {Error, [{value, ValueSeen}, {path, Path2}]}}).
+
+
+-compile({nowarn_unused_function,prettify_path/1}).
+prettify_path([]) -> top_level;
+prettify_path(PathR) ->
+ list_to_atom(string:join(lists:map(fun atom_to_list/1,
+ lists:reverse(PathR)),
+ ".")).
+
+
+-compile({nowarn_unused_function,id/2}).
+-compile({inline,id/2}).
+id(X, _TrUserData) -> X.
+
+-compile({nowarn_unused_function,v_ok/3}).
+-compile({inline,v_ok/3}).
+v_ok(_Value, _Path, _TrUserData) -> ok.
+
+-compile({nowarn_unused_function,m_overwrite/3}).
+-compile({inline,m_overwrite/3}).
+m_overwrite(_Prev, New, _TrUserData) -> New.
+
+-compile({nowarn_unused_function,cons/3}).
+-compile({inline,cons/3}).
+cons(Elem, Acc, _TrUserData) -> [Elem | Acc].
+
+-compile({nowarn_unused_function,lists_reverse/2}).
+-compile({inline,lists_reverse/2}).
+'lists_reverse'(L, _TrUserData) -> lists:reverse(L).
+-compile({nowarn_unused_function,'erlang_++'/3}).
+-compile({inline,'erlang_++'/3}).
+'erlang_++'(A, B, _TrUserData) -> A ++ B.
+
+get_msg_defs() ->
+ [{{enum, 'RetirementReason'},
+ [{'RETIRED_OTHER', 0}, {'RETIRED_INVALID', 1},
+ {'RETIRED_SECURITY', 2}, {'RETIRED_DEPRECATED', 3},
+ {'RETIRED_RENAMED', 4}]},
+ {{msg, 'Package'},
+ [#{name => releases, fnum => 1, rnum => 2,
+ type => {msg, 'Release'}, occurrence => repeated,
+ opts => []},
+ #{name => name, fnum => 2, rnum => 3, type => string,
+ occurrence => required, opts => []},
+ #{name => repository, fnum => 3, rnum => 4,
+ type => string, occurrence => required, opts => []}]},
+ {{msg, 'Release'},
+ [#{name => version, fnum => 1, rnum => 2,
+ type => string, occurrence => required, opts => []},
+ #{name => checksum, fnum => 2, rnum => 3, type => bytes,
+ occurrence => required, opts => []},
+ #{name => dependencies, fnum => 3, rnum => 4,
+ type => {msg, 'Dependency'}, occurrence => repeated,
+ opts => []},
+ #{name => retired, fnum => 4, rnum => 5,
+ type => {msg, 'RetirementStatus'},
+ occurrence => optional, opts => []}]},
+ {{msg, 'RetirementStatus'},
+ [#{name => reason, fnum => 1, rnum => 2,
+ type => {enum, 'RetirementReason'},
+ occurrence => required, opts => []},
+ #{name => message, fnum => 2, rnum => 3, type => string,
+ occurrence => optional, opts => []}]},
+ {{msg, 'Dependency'},
+ [#{name => package, fnum => 1, rnum => 2,
+ type => string, occurrence => required, opts => []},
+ #{name => requirement, fnum => 2, rnum => 3,
+ type => string, occurrence => required, opts => []},
+ #{name => optional, fnum => 3, rnum => 4, type => bool,
+ occurrence => optional, opts => []},
+ #{name => app, fnum => 4, rnum => 5, type => string,
+ occurrence => optional, opts => []},
+ #{name => repository, fnum => 5, rnum => 6,
+ type => string, occurrence => optional, opts => []}]}].
+
+
+get_msg_names() ->
+ ['Package', 'Release', 'RetirementStatus',
+ 'Dependency'].
+
+
+get_group_names() -> [].
+
+
+get_msg_or_group_names() ->
+ ['Package', 'Release', 'RetirementStatus',
+ 'Dependency'].
+
+
+get_enum_names() -> ['RetirementReason'].
+
+
+fetch_msg_def(MsgName) ->
+ case find_msg_def(MsgName) of
+ Fs when is_list(Fs) -> Fs;
+ error -> erlang:error({no_such_msg, MsgName})
+ end.
+
+
+fetch_enum_def(EnumName) ->
+ case find_enum_def(EnumName) of
+ Es when is_list(Es) -> Es;
+ error -> erlang:error({no_such_enum, EnumName})
+ end.
+
+
+find_msg_def('Package') ->
+ [#{name => releases, fnum => 1, rnum => 2,
+ type => {msg, 'Release'}, occurrence => repeated,
+ opts => []},
+ #{name => name, fnum => 2, rnum => 3, type => string,
+ occurrence => required, opts => []},
+ #{name => repository, fnum => 3, rnum => 4,
+ type => string, occurrence => required, opts => []}];
+find_msg_def('Release') ->
+ [#{name => version, fnum => 1, rnum => 2,
+ type => string, occurrence => required, opts => []},
+ #{name => checksum, fnum => 2, rnum => 3, type => bytes,
+ occurrence => required, opts => []},
+ #{name => dependencies, fnum => 3, rnum => 4,
+ type => {msg, 'Dependency'}, occurrence => repeated,
+ opts => []},
+ #{name => retired, fnum => 4, rnum => 5,
+ type => {msg, 'RetirementStatus'},
+ occurrence => optional, opts => []}];
+find_msg_def('RetirementStatus') ->
+ [#{name => reason, fnum => 1, rnum => 2,
+ type => {enum, 'RetirementReason'},
+ occurrence => required, opts => []},
+ #{name => message, fnum => 2, rnum => 3, type => string,
+ occurrence => optional, opts => []}];
+find_msg_def('Dependency') ->
+ [#{name => package, fnum => 1, rnum => 2,
+ type => string, occurrence => required, opts => []},
+ #{name => requirement, fnum => 2, rnum => 3,
+ type => string, occurrence => required, opts => []},
+ #{name => optional, fnum => 3, rnum => 4, type => bool,
+ occurrence => optional, opts => []},
+ #{name => app, fnum => 4, rnum => 5, type => string,
+ occurrence => optional, opts => []},
+ #{name => repository, fnum => 5, rnum => 6,
+ type => string, occurrence => optional, opts => []}];
+find_msg_def(_) -> error.
+
+
+find_enum_def('RetirementReason') ->
+ [{'RETIRED_OTHER', 0}, {'RETIRED_INVALID', 1},
+ {'RETIRED_SECURITY', 2}, {'RETIRED_DEPRECATED', 3},
+ {'RETIRED_RENAMED', 4}];
+find_enum_def(_) -> error.
+
+
+enum_symbol_by_value('RetirementReason', Value) ->
+ enum_symbol_by_value_RetirementReason(Value).
+
+
+enum_value_by_symbol('RetirementReason', Sym) ->
+ enum_value_by_symbol_RetirementReason(Sym).
+
+
+enum_symbol_by_value_RetirementReason(0) ->
+ 'RETIRED_OTHER';
+enum_symbol_by_value_RetirementReason(1) ->
+ 'RETIRED_INVALID';
+enum_symbol_by_value_RetirementReason(2) ->
+ 'RETIRED_SECURITY';
+enum_symbol_by_value_RetirementReason(3) ->
+ 'RETIRED_DEPRECATED';
+enum_symbol_by_value_RetirementReason(4) ->
+ 'RETIRED_RENAMED'.
+
+
+enum_value_by_symbol_RetirementReason('RETIRED_OTHER') ->
+ 0;
+enum_value_by_symbol_RetirementReason('RETIRED_INVALID') ->
+ 1;
+enum_value_by_symbol_RetirementReason('RETIRED_SECURITY') ->
+ 2;
+enum_value_by_symbol_RetirementReason('RETIRED_DEPRECATED') ->
+ 3;
+enum_value_by_symbol_RetirementReason('RETIRED_RENAMED') ->
+ 4.
+
+
+get_service_names() -> [].
+
+
+get_service_def(_) -> error.
+
+
+get_rpc_names(_) -> error.
+
+
+find_rpc_def(_, _) -> error.
+
+
+
+-spec fetch_rpc_def(_, _) -> no_return().
+fetch_rpc_def(ServiceName, RpcName) ->
+ erlang:error({no_such_rpc, ServiceName, RpcName}).
+
+
+get_package_name() -> undefined.
+
+
+
+gpb_version_as_string() ->
+ "4.3.1".
+
+gpb_version_as_list() ->
+ [4,3,1].
diff --git a/src/r3_hex_pb_signed.erl b/src/r3_hex_pb_signed.erl
new file mode 100644
index 0000000..e8b188a
--- /dev/null
+++ b/src/r3_hex_pb_signed.erl
@@ -0,0 +1,564 @@
+%% Vendored from hex_core v0.5.0, do not edit manually
+
+%% -*- coding: utf-8 -*-
+%% Automatically generated, do not edit
+%% Generated by gpb_compile version 4.3.1
+-module(r3_hex_pb_signed).
+
+-export([encode_msg/2, encode_msg/3]).
+-export([decode_msg/2, decode_msg/3]).
+-export([merge_msgs/3, merge_msgs/4]).
+-export([verify_msg/2, verify_msg/3]).
+-export([get_msg_defs/0]).
+-export([get_msg_names/0]).
+-export([get_group_names/0]).
+-export([get_msg_or_group_names/0]).
+-export([get_enum_names/0]).
+-export([find_msg_def/1, fetch_msg_def/1]).
+-export([find_enum_def/1, fetch_enum_def/1]).
+-export([enum_symbol_by_value/2, enum_value_by_symbol/2]).
+-export([get_service_names/0]).
+-export([get_service_def/1]).
+-export([get_rpc_names/1]).
+-export([find_rpc_def/2, fetch_rpc_def/2]).
+-export([get_package_name/0]).
+-export([gpb_version_as_string/0, gpb_version_as_list/0]).
+
+
+%% enumerated types
+
+-export_type([]).
+
+%% message types
+-type 'Signed'() ::
+ #{payload => iodata() % = 1
+ %% signature => iodata() % = 2
+ }.
+
+-export_type(['Signed'/0]).
+
+-spec encode_msg('Signed'(), atom()) -> binary().
+encode_msg(Msg, MsgName) when is_atom(MsgName) ->
+ encode_msg(Msg, MsgName, []).
+
+-spec encode_msg('Signed'(), atom(), list()) -> binary().
+encode_msg(Msg, MsgName, Opts) ->
+ verify_msg(Msg, MsgName, Opts),
+ TrUserData = proplists:get_value(user_data, Opts),
+ case MsgName of
+ 'Signed' ->
+ e_msg_Signed(id(Msg, TrUserData), TrUserData)
+ end.
+
+
+e_msg_Signed(Msg, TrUserData) ->
+ e_msg_Signed(Msg, <<>>, TrUserData).
+
+
+e_msg_Signed(#{payload := F1} = M, Bin, TrUserData) ->
+ B1 = begin
+ TrF1 = id(F1, TrUserData),
+ e_type_bytes(TrF1, <<Bin/binary, 10>>, TrUserData)
+ end,
+ case M of
+ #{signature := F2} ->
+ begin
+ TrF2 = id(F2, TrUserData),
+ e_type_bytes(TrF2, <<B1/binary, 18>>, TrUserData)
+ end;
+ _ -> B1
+ end.
+
+-compile({nowarn_unused_function,e_type_sint/3}).
+e_type_sint(Value, Bin, _TrUserData) when Value >= 0 ->
+ e_varint(Value * 2, Bin);
+e_type_sint(Value, Bin, _TrUserData) ->
+ e_varint(Value * -2 - 1, Bin).
+
+-compile({nowarn_unused_function,e_type_int32/3}).
+e_type_int32(Value, Bin, _TrUserData)
+ when 0 =< Value, Value =< 127 ->
+ <<Bin/binary, Value>>;
+e_type_int32(Value, Bin, _TrUserData) ->
+ <<N:64/unsigned-native>> = <<Value:64/signed-native>>,
+ e_varint(N, Bin).
+
+-compile({nowarn_unused_function,e_type_int64/3}).
+e_type_int64(Value, Bin, _TrUserData)
+ when 0 =< Value, Value =< 127 ->
+ <<Bin/binary, Value>>;
+e_type_int64(Value, Bin, _TrUserData) ->
+ <<N:64/unsigned-native>> = <<Value:64/signed-native>>,
+ e_varint(N, Bin).
+
+-compile({nowarn_unused_function,e_type_bool/3}).
+e_type_bool(true, Bin, _TrUserData) ->
+ <<Bin/binary, 1>>;
+e_type_bool(false, Bin, _TrUserData) ->
+ <<Bin/binary, 0>>;
+e_type_bool(1, Bin, _TrUserData) -> <<Bin/binary, 1>>;
+e_type_bool(0, Bin, _TrUserData) -> <<Bin/binary, 0>>.
+
+-compile({nowarn_unused_function,e_type_string/3}).
+e_type_string(S, Bin, _TrUserData) ->
+ Utf8 = unicode:characters_to_binary(S),
+ Bin2 = e_varint(byte_size(Utf8), Bin),
+ <<Bin2/binary, Utf8/binary>>.
+
+-compile({nowarn_unused_function,e_type_bytes/3}).
+e_type_bytes(Bytes, Bin, _TrUserData)
+ when is_binary(Bytes) ->
+ Bin2 = e_varint(byte_size(Bytes), Bin),
+ <<Bin2/binary, Bytes/binary>>;
+e_type_bytes(Bytes, Bin, _TrUserData)
+ when is_list(Bytes) ->
+ BytesBin = iolist_to_binary(Bytes),
+ Bin2 = e_varint(byte_size(BytesBin), Bin),
+ <<Bin2/binary, BytesBin/binary>>.
+
+-compile({nowarn_unused_function,e_type_fixed32/3}).
+e_type_fixed32(Value, Bin, _TrUserData) ->
+ <<Bin/binary, Value:32/little>>.
+
+-compile({nowarn_unused_function,e_type_sfixed32/3}).
+e_type_sfixed32(Value, Bin, _TrUserData) ->
+ <<Bin/binary, Value:32/little-signed>>.
+
+-compile({nowarn_unused_function,e_type_fixed64/3}).
+e_type_fixed64(Value, Bin, _TrUserData) ->
+ <<Bin/binary, Value:64/little>>.
+
+-compile({nowarn_unused_function,e_type_sfixed64/3}).
+e_type_sfixed64(Value, Bin, _TrUserData) ->
+ <<Bin/binary, Value:64/little-signed>>.
+
+-compile({nowarn_unused_function,e_type_float/3}).
+e_type_float(V, Bin, _) when is_number(V) ->
+ <<Bin/binary, V:32/little-float>>;
+e_type_float(infinity, Bin, _) ->
+ <<Bin/binary, 0:16, 128, 127>>;
+e_type_float('-infinity', Bin, _) ->
+ <<Bin/binary, 0:16, 128, 255>>;
+e_type_float(nan, Bin, _) ->
+ <<Bin/binary, 0:16, 192, 127>>.
+
+-compile({nowarn_unused_function,e_type_double/3}).
+e_type_double(V, Bin, _) when is_number(V) ->
+ <<Bin/binary, V:64/little-float>>;
+e_type_double(infinity, Bin, _) ->
+ <<Bin/binary, 0:48, 240, 127>>;
+e_type_double('-infinity', Bin, _) ->
+ <<Bin/binary, 0:48, 240, 255>>;
+e_type_double(nan, Bin, _) ->
+ <<Bin/binary, 0:48, 248, 127>>.
+
+-compile({nowarn_unused_function,e_varint/3}).
+e_varint(N, Bin, _TrUserData) -> e_varint(N, Bin).
+
+-compile({nowarn_unused_function,e_varint/2}).
+e_varint(N, Bin) when N =< 127 -> <<Bin/binary, N>>;
+e_varint(N, Bin) ->
+ Bin2 = <<Bin/binary, (N band 127 bor 128)>>,
+ e_varint(N bsr 7, Bin2).
+
+
+decode_msg(Bin, MsgName) when is_binary(Bin) ->
+ decode_msg(Bin, MsgName, []).
+
+decode_msg(Bin, MsgName, Opts) when is_binary(Bin) ->
+ TrUserData = proplists:get_value(user_data, Opts),
+ decode_msg_1_catch(Bin, MsgName, TrUserData).
+
+-ifdef('OTP_RELEASE').
+decode_msg_1_catch(Bin, MsgName, TrUserData) ->
+ try decode_msg_2_doit(MsgName, Bin, TrUserData)
+ catch Class:Reason:StackTrace -> error({gpb_error,{decoding_failure, {Bin, MsgName, {Class, Reason, StackTrace}}}})
+ end.
+-else.
+-ifdef('GPB_PATTERN_STACK').
+decode_msg_1_catch(Bin, MsgName, TrUserData) ->
+ try decode_msg_2_doit(MsgName, Bin, TrUserData)
+ catch Class:Reason:StackTrace -> error({gpb_error,{decoding_failure, {Bin, MsgName, {Class, Reason, StackTrace}}}})
+ end.
+-else.
+decode_msg_1_catch(Bin, MsgName, TrUserData) ->
+ try decode_msg_2_doit(MsgName, Bin, TrUserData)
+ catch Class:Reason ->
+ StackTrace = erlang:get_stacktrace(),
+ error({gpb_error,{decoding_failure, {Bin, MsgName, {Class, Reason, StackTrace}}}})
+ end.
+-endif.
+
+-endif.
+
+decode_msg_2_doit('Signed', Bin, TrUserData) ->
+ id(d_msg_Signed(Bin, TrUserData), TrUserData).
+
+
+
+d_msg_Signed(Bin, TrUserData) ->
+ dfp_read_field_def_Signed(Bin, 0, 0,
+ id('$undef', TrUserData),
+ id('$undef', TrUserData), TrUserData).
+
+dfp_read_field_def_Signed(<<10, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, TrUserData) ->
+ d_field_Signed_payload(Rest, Z1, Z2, F@_1, F@_2,
+ TrUserData);
+dfp_read_field_def_Signed(<<18, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, TrUserData) ->
+ d_field_Signed_signature(Rest, Z1, Z2, F@_1, F@_2,
+ TrUserData);
+dfp_read_field_def_Signed(<<>>, 0, 0, F@_1, F@_2, _) ->
+ S1 = #{payload => F@_1},
+ if F@_2 == '$undef' -> S1;
+ true -> S1#{signature => F@_2}
+ end;
+dfp_read_field_def_Signed(Other, Z1, Z2, F@_1, F@_2,
+ TrUserData) ->
+ dg_read_field_def_Signed(Other, Z1, Z2, F@_1, F@_2,
+ TrUserData).
+
+dg_read_field_def_Signed(<<1:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, TrUserData)
+ when N < 32 - 7 ->
+ dg_read_field_def_Signed(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, TrUserData);
+dg_read_field_def_Signed(<<0:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, TrUserData) ->
+ Key = X bsl N + Acc,
+ case Key of
+ 10 ->
+ d_field_Signed_payload(Rest, 0, 0, F@_1, F@_2,
+ TrUserData);
+ 18 ->
+ d_field_Signed_signature(Rest, 0, 0, F@_1, F@_2,
+ TrUserData);
+ _ ->
+ case Key band 7 of
+ 0 ->
+ skip_varint_Signed(Rest, 0, 0, F@_1, F@_2, TrUserData);
+ 1 -> skip_64_Signed(Rest, 0, 0, F@_1, F@_2, TrUserData);
+ 2 ->
+ skip_length_delimited_Signed(Rest, 0, 0, F@_1, F@_2,
+ TrUserData);
+ 3 ->
+ skip_group_Signed(Rest, Key bsr 3, 0, F@_1, F@_2,
+ TrUserData);
+ 5 -> skip_32_Signed(Rest, 0, 0, F@_1, F@_2, TrUserData)
+ end
+ end;
+dg_read_field_def_Signed(<<>>, 0, 0, F@_1, F@_2, _) ->
+ S1 = #{payload => F@_1},
+ if F@_2 == '$undef' -> S1;
+ true -> S1#{signature => F@_2}
+ end.
+
+d_field_Signed_payload(<<1:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, TrUserData)
+ when N < 57 ->
+ d_field_Signed_payload(Rest, N + 7, X bsl N + Acc, F@_1,
+ F@_2, TrUserData);
+d_field_Signed_payload(<<0:1, X:7, Rest/binary>>, N,
+ Acc, _, F@_2, TrUserData) ->
+ {NewFValue, RestF} = begin
+ Len = X bsl N + Acc,
+ <<Bytes:Len/binary, Rest2/binary>> = Rest,
+ {id(binary:copy(Bytes), TrUserData), Rest2}
+ end,
+ dfp_read_field_def_Signed(RestF, 0, 0, NewFValue, F@_2,
+ TrUserData).
+
+d_field_Signed_signature(<<1:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, TrUserData)
+ when N < 57 ->
+ d_field_Signed_signature(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, TrUserData);
+d_field_Signed_signature(<<0:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, _, TrUserData) ->
+ {NewFValue, RestF} = begin
+ Len = X bsl N + Acc,
+ <<Bytes:Len/binary, Rest2/binary>> = Rest,
+ {id(binary:copy(Bytes), TrUserData), Rest2}
+ end,
+ dfp_read_field_def_Signed(RestF, 0, 0, F@_1, NewFValue,
+ TrUserData).
+
+skip_varint_Signed(<<1:1, _:7, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, TrUserData) ->
+ skip_varint_Signed(Rest, Z1, Z2, F@_1, F@_2,
+ TrUserData);
+skip_varint_Signed(<<0:1, _:7, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, TrUserData) ->
+ dfp_read_field_def_Signed(Rest, Z1, Z2, F@_1, F@_2,
+ TrUserData).
+
+skip_length_delimited_Signed(<<1:1, X:7, Rest/binary>>,
+ N, Acc, F@_1, F@_2, TrUserData)
+ when N < 57 ->
+ skip_length_delimited_Signed(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, TrUserData);
+skip_length_delimited_Signed(<<0:1, X:7, Rest/binary>>,
+ N, Acc, F@_1, F@_2, TrUserData) ->
+ Length = X bsl N + Acc,
+ <<_:Length/binary, Rest2/binary>> = Rest,
+ dfp_read_field_def_Signed(Rest2, 0, 0, F@_1, F@_2,
+ TrUserData).
+
+skip_group_Signed(Bin, FNum, Z2, F@_1, F@_2,
+ TrUserData) ->
+ {_, Rest} = read_group(Bin, FNum),
+ dfp_read_field_def_Signed(Rest, 0, Z2, F@_1, F@_2,
+ TrUserData).
+
+skip_32_Signed(<<_:32, Rest/binary>>, Z1, Z2, F@_1,
+ F@_2, TrUserData) ->
+ dfp_read_field_def_Signed(Rest, Z1, Z2, F@_1, F@_2,
+ TrUserData).
+
+skip_64_Signed(<<_:64, Rest/binary>>, Z1, Z2, F@_1,
+ F@_2, TrUserData) ->
+ dfp_read_field_def_Signed(Rest, Z1, Z2, F@_1, F@_2,
+ TrUserData).
+
+read_group(Bin, FieldNum) ->
+ {NumBytes, EndTagLen} = read_gr_b(Bin, 0, 0, 0, 0, FieldNum),
+ <<Group:NumBytes/binary, _:EndTagLen/binary, Rest/binary>> = Bin,
+ {Group, Rest}.
+
+%% Like skipping over fields, but record the total length,
+%% Each field is <(FieldNum bsl 3) bor FieldType> ++ <FieldValue>
+%% Record the length because varints may be non-optimally encoded.
+%%
+%% Groups can be nested, but assume the same FieldNum cannot be nested
+%% because group field numbers are shared with the rest of the fields
+%% numbers. Thus we can search just for an group-end with the same
+%% field number.
+%%
+%% (The only time the same group field number could occur would
+%% be in a nested sub message, but then it would be inside a
+%% length-delimited entry, which we skip-read by length.)
+read_gr_b(<<1:1, X:7, Tl/binary>>, N, Acc, NumBytes, TagLen, FieldNum)
+ when N < (32-7) ->
+ read_gr_b(Tl, N+7, X bsl N + Acc, NumBytes, TagLen+1, FieldNum);
+read_gr_b(<<0:1, X:7, Tl/binary>>, N, Acc, NumBytes, TagLen,
+ FieldNum) ->
+ Key = X bsl N + Acc,
+ TagLen1 = TagLen + 1,
+ case {Key bsr 3, Key band 7} of
+ {FieldNum, 4} -> % 4 = group_end
+ {NumBytes, TagLen1};
+ {_, 0} -> % 0 = varint
+ read_gr_vi(Tl, 0, NumBytes + TagLen1, FieldNum);
+ {_, 1} -> % 1 = bits64
+ <<_:64, Tl2/binary>> = Tl,
+ read_gr_b(Tl2, 0, 0, NumBytes + TagLen1 + 8, 0, FieldNum);
+ {_, 2} -> % 2 = length_delimited
+ read_gr_ld(Tl, 0, 0, NumBytes + TagLen1, FieldNum);
+ {_, 3} -> % 3 = group_start
+ read_gr_b(Tl, 0, 0, NumBytes + TagLen1, 0, FieldNum);
+ {_, 4} -> % 4 = group_end
+ read_gr_b(Tl, 0, 0, NumBytes + TagLen1, 0, FieldNum);
+ {_, 5} -> % 5 = bits32
+ <<_:32, Tl2/binary>> = Tl,
+ read_gr_b(Tl2, 0, 0, NumBytes + TagLen1 + 4, 0, FieldNum)
+ end.
+
+read_gr_vi(<<1:1, _:7, Tl/binary>>, N, NumBytes, FieldNum)
+ when N < (64-7) ->
+ read_gr_vi(Tl, N+7, NumBytes+1, FieldNum);
+read_gr_vi(<<0:1, _:7, Tl/binary>>, _, NumBytes, FieldNum) ->
+ read_gr_b(Tl, 0, 0, NumBytes+1, 0, FieldNum).
+
+read_gr_ld(<<1:1, X:7, Tl/binary>>, N, Acc, NumBytes, FieldNum)
+ when N < (64-7) ->
+ read_gr_ld(Tl, N+7, X bsl N + Acc, NumBytes+1, FieldNum);
+read_gr_ld(<<0:1, X:7, Tl/binary>>, N, Acc, NumBytes, FieldNum) ->
+ Len = X bsl N + Acc,
+ NumBytes1 = NumBytes + 1,
+ <<_:Len/binary, Tl2/binary>> = Tl,
+ read_gr_b(Tl2, 0, 0, NumBytes1 + Len, 0, FieldNum).
+
+merge_msgs(Prev, New, MsgName) when is_atom(MsgName) ->
+ merge_msgs(Prev, New, MsgName, []).
+
+merge_msgs(Prev, New, MsgName, Opts) ->
+ TrUserData = proplists:get_value(user_data, Opts),
+ case MsgName of
+ 'Signed' -> merge_msg_Signed(Prev, New, TrUserData)
+ end.
+
+-compile({nowarn_unused_function,merge_msg_Signed/3}).
+merge_msg_Signed(#{} = PMsg,
+ #{payload := NFpayload} = NMsg, _) ->
+ S1 = #{payload => NFpayload},
+ case {PMsg, NMsg} of
+ {_, #{signature := NFsignature}} ->
+ S1#{signature => NFsignature};
+ {#{signature := PFsignature}, _} ->
+ S1#{signature => PFsignature};
+ _ -> S1
+ end.
+
+
+verify_msg(Msg, MsgName) when is_atom(MsgName) ->
+ verify_msg(Msg, MsgName, []).
+
+verify_msg(Msg, MsgName, Opts) ->
+ TrUserData = proplists:get_value(user_data, Opts),
+ case MsgName of
+ 'Signed' -> v_msg_Signed(Msg, [MsgName], TrUserData);
+ _ -> mk_type_error(not_a_known_message, Msg, [])
+ end.
+
+
+-compile({nowarn_unused_function,v_msg_Signed/3}).
+v_msg_Signed(#{payload := F1} = M, Path, TrUserData) ->
+ v_type_bytes(F1, [payload | Path], TrUserData),
+ case M of
+ #{signature := F2} ->
+ v_type_bytes(F2, [signature | Path], TrUserData);
+ _ -> ok
+ end,
+ lists:foreach(fun (payload) -> ok;
+ (signature) -> ok;
+ (OtherKey) ->
+ mk_type_error({extraneous_key, OtherKey}, M, Path)
+ end,
+ maps:keys(M)),
+ ok;
+v_msg_Signed(M, Path, _TrUserData) when is_map(M) ->
+ mk_type_error({missing_fields,
+ [payload] -- maps:keys(M), 'Signed'},
+ M, Path);
+v_msg_Signed(X, Path, _TrUserData) ->
+ mk_type_error({expected_msg, 'Signed'}, X, Path).
+
+-compile({nowarn_unused_function,v_type_bytes/3}).
+v_type_bytes(B, _Path, _TrUserData) when is_binary(B) ->
+ ok;
+v_type_bytes(B, _Path, _TrUserData) when is_list(B) ->
+ ok;
+v_type_bytes(X, Path, _TrUserData) ->
+ mk_type_error(bad_binary_value, X, Path).
+
+-compile({nowarn_unused_function,mk_type_error/3}).
+-spec mk_type_error(_, _, list()) -> no_return().
+mk_type_error(Error, ValueSeen, Path) ->
+ Path2 = prettify_path(Path),
+ erlang:error({gpb_type_error,
+ {Error, [{value, ValueSeen}, {path, Path2}]}}).
+
+
+-compile({nowarn_unused_function,prettify_path/1}).
+prettify_path([]) -> top_level;
+prettify_path(PathR) ->
+ list_to_atom(string:join(lists:map(fun atom_to_list/1,
+ lists:reverse(PathR)),
+ ".")).
+
+
+-compile({nowarn_unused_function,id/2}).
+-compile({inline,id/2}).
+id(X, _TrUserData) -> X.
+
+-compile({nowarn_unused_function,v_ok/3}).
+-compile({inline,v_ok/3}).
+v_ok(_Value, _Path, _TrUserData) -> ok.
+
+-compile({nowarn_unused_function,m_overwrite/3}).
+-compile({inline,m_overwrite/3}).
+m_overwrite(_Prev, New, _TrUserData) -> New.
+
+-compile({nowarn_unused_function,cons/3}).
+-compile({inline,cons/3}).
+cons(Elem, Acc, _TrUserData) -> [Elem | Acc].
+
+-compile({nowarn_unused_function,lists_reverse/2}).
+-compile({inline,lists_reverse/2}).
+'lists_reverse'(L, _TrUserData) -> lists:reverse(L).
+-compile({nowarn_unused_function,'erlang_++'/3}).
+-compile({inline,'erlang_++'/3}).
+'erlang_++'(A, B, _TrUserData) -> A ++ B.
+
+get_msg_defs() ->
+ [{{msg, 'Signed'},
+ [#{name => payload, fnum => 1, rnum => 2, type => bytes,
+ occurrence => required, opts => []},
+ #{name => signature, fnum => 2, rnum => 3,
+ type => bytes, occurrence => optional, opts => []}]}].
+
+
+get_msg_names() -> ['Signed'].
+
+
+get_group_names() -> [].
+
+
+get_msg_or_group_names() -> ['Signed'].
+
+
+get_enum_names() -> [].
+
+
+fetch_msg_def(MsgName) ->
+ case find_msg_def(MsgName) of
+ Fs when is_list(Fs) -> Fs;
+ error -> erlang:error({no_such_msg, MsgName})
+ end.
+
+
+-spec fetch_enum_def(_) -> no_return().
+fetch_enum_def(EnumName) ->
+ erlang:error({no_such_enum, EnumName}).
+
+
+find_msg_def('Signed') ->
+ [#{name => payload, fnum => 1, rnum => 2, type => bytes,
+ occurrence => required, opts => []},
+ #{name => signature, fnum => 2, rnum => 3,
+ type => bytes, occurrence => optional, opts => []}];
+find_msg_def(_) -> error.
+
+
+find_enum_def(_) -> error.
+
+
+-spec enum_symbol_by_value(_, _) -> no_return().
+enum_symbol_by_value(E, V) ->
+ erlang:error({no_enum_defs, E, V}).
+
+
+-spec enum_value_by_symbol(_, _) -> no_return().
+enum_value_by_symbol(E, V) ->
+ erlang:error({no_enum_defs, E, V}).
+
+
+
+get_service_names() -> [].
+
+
+get_service_def(_) -> error.
+
+
+get_rpc_names(_) -> error.
+
+
+find_rpc_def(_, _) -> error.
+
+
+
+-spec fetch_rpc_def(_, _) -> no_return().
+fetch_rpc_def(ServiceName, RpcName) ->
+ erlang:error({no_such_rpc, ServiceName, RpcName}).
+
+
+get_package_name() -> undefined.
+
+
+
+gpb_version_as_string() ->
+ "4.3.1".
+
+gpb_version_as_list() ->
+ [4,3,1].
diff --git a/src/r3_hex_pb_versions.erl b/src/r3_hex_pb_versions.erl
new file mode 100644
index 0000000..66407dd
--- /dev/null
+++ b/src/r3_hex_pb_versions.erl
@@ -0,0 +1,958 @@
+%% Vendored from hex_core v0.5.0, do not edit manually
+
+%% -*- coding: utf-8 -*-
+%% Automatically generated, do not edit
+%% Generated by gpb_compile version 4.3.1
+-module(r3_hex_pb_versions).
+
+-export([encode_msg/2, encode_msg/3]).
+-export([decode_msg/2, decode_msg/3]).
+-export([merge_msgs/3, merge_msgs/4]).
+-export([verify_msg/2, verify_msg/3]).
+-export([get_msg_defs/0]).
+-export([get_msg_names/0]).
+-export([get_group_names/0]).
+-export([get_msg_or_group_names/0]).
+-export([get_enum_names/0]).
+-export([find_msg_def/1, fetch_msg_def/1]).
+-export([find_enum_def/1, fetch_enum_def/1]).
+-export([enum_symbol_by_value/2, enum_value_by_symbol/2]).
+-export([get_service_names/0]).
+-export([get_service_def/1]).
+-export([get_rpc_names/1]).
+-export([find_rpc_def/2, fetch_rpc_def/2]).
+-export([get_package_name/0]).
+-export([gpb_version_as_string/0, gpb_version_as_list/0]).
+
+
+%% enumerated types
+
+-export_type([]).
+
+%% message types
+-type 'Versions'() ::
+ #{packages => ['Package'()], % = 1
+ repository => iodata() % = 2
+ }.
+
+-type 'Package'() ::
+ #{name => iodata(), % = 1
+ versions => [iodata()], % = 2
+ retired => [integer()] % = 3, 32 bits
+ }.
+
+-export_type(['Versions'/0, 'Package'/0]).
+
+-spec encode_msg('Versions'() | 'Package'(), atom()) -> binary().
+encode_msg(Msg, MsgName) when is_atom(MsgName) ->
+ encode_msg(Msg, MsgName, []).
+
+-spec encode_msg('Versions'() | 'Package'(), atom(), list()) -> binary().
+encode_msg(Msg, MsgName, Opts) ->
+ verify_msg(Msg, MsgName, Opts),
+ TrUserData = proplists:get_value(user_data, Opts),
+ case MsgName of
+ 'Versions' ->
+ e_msg_Versions(id(Msg, TrUserData), TrUserData);
+ 'Package' ->
+ e_msg_Package(id(Msg, TrUserData), TrUserData)
+ end.
+
+
+e_msg_Versions(Msg, TrUserData) ->
+ e_msg_Versions(Msg, <<>>, TrUserData).
+
+
+e_msg_Versions(#{repository := F2} = M, Bin,
+ TrUserData) ->
+ B1 = case M of
+ #{packages := F1} ->
+ TrF1 = id(F1, TrUserData),
+ if TrF1 == [] -> Bin;
+ true -> e_field_Versions_packages(TrF1, Bin, TrUserData)
+ end;
+ _ -> Bin
+ end,
+ begin
+ TrF2 = id(F2, TrUserData),
+ e_type_string(TrF2, <<B1/binary, 18>>, TrUserData)
+ end.
+
+e_msg_Package(Msg, TrUserData) ->
+ e_msg_Package(Msg, <<>>, TrUserData).
+
+
+e_msg_Package(#{name := F1} = M, Bin, TrUserData) ->
+ B1 = begin
+ TrF1 = id(F1, TrUserData),
+ e_type_string(TrF1, <<Bin/binary, 10>>, TrUserData)
+ end,
+ B2 = case M of
+ #{versions := F2} ->
+ TrF2 = id(F2, TrUserData),
+ if TrF2 == [] -> B1;
+ true -> e_field_Package_versions(TrF2, B1, TrUserData)
+ end;
+ _ -> B1
+ end,
+ case M of
+ #{retired := F3} ->
+ TrF3 = id(F3, TrUserData),
+ if TrF3 == [] -> B2;
+ true -> e_field_Package_retired(TrF3, B2, TrUserData)
+ end;
+ _ -> B2
+ end.
+
+e_mfield_Versions_packages(Msg, Bin, TrUserData) ->
+ SubBin = e_msg_Package(Msg, <<>>, TrUserData),
+ Bin2 = e_varint(byte_size(SubBin), Bin),
+ <<Bin2/binary, SubBin/binary>>.
+
+e_field_Versions_packages([Elem | Rest], Bin,
+ TrUserData) ->
+ Bin2 = <<Bin/binary, 10>>,
+ Bin3 = e_mfield_Versions_packages(id(Elem, TrUserData),
+ Bin2, TrUserData),
+ e_field_Versions_packages(Rest, Bin3, TrUserData);
+e_field_Versions_packages([], Bin, _TrUserData) -> Bin.
+
+e_field_Package_versions([Elem | Rest], Bin,
+ TrUserData) ->
+ Bin2 = <<Bin/binary, 18>>,
+ Bin3 = e_type_string(id(Elem, TrUserData), Bin2,
+ TrUserData),
+ e_field_Package_versions(Rest, Bin3, TrUserData);
+e_field_Package_versions([], Bin, _TrUserData) -> Bin.
+
+e_field_Package_retired(Elems, Bin, TrUserData)
+ when Elems =/= [] ->
+ SubBin = e_pfield_Package_retired(Elems, <<>>,
+ TrUserData),
+ Bin2 = <<Bin/binary, 26>>,
+ Bin3 = e_varint(byte_size(SubBin), Bin2),
+ <<Bin3/binary, SubBin/binary>>;
+e_field_Package_retired([], Bin, _TrUserData) -> Bin.
+
+e_pfield_Package_retired([Value | Rest], Bin,
+ TrUserData) ->
+ Bin2 = e_type_int32(id(Value, TrUserData), Bin,
+ TrUserData),
+ e_pfield_Package_retired(Rest, Bin2, TrUserData);
+e_pfield_Package_retired([], Bin, _TrUserData) -> Bin.
+
+-compile({nowarn_unused_function,e_type_sint/3}).
+e_type_sint(Value, Bin, _TrUserData) when Value >= 0 ->
+ e_varint(Value * 2, Bin);
+e_type_sint(Value, Bin, _TrUserData) ->
+ e_varint(Value * -2 - 1, Bin).
+
+-compile({nowarn_unused_function,e_type_int32/3}).
+e_type_int32(Value, Bin, _TrUserData)
+ when 0 =< Value, Value =< 127 ->
+ <<Bin/binary, Value>>;
+e_type_int32(Value, Bin, _TrUserData) ->
+ <<N:64/unsigned-native>> = <<Value:64/signed-native>>,
+ e_varint(N, Bin).
+
+-compile({nowarn_unused_function,e_type_int64/3}).
+e_type_int64(Value, Bin, _TrUserData)
+ when 0 =< Value, Value =< 127 ->
+ <<Bin/binary, Value>>;
+e_type_int64(Value, Bin, _TrUserData) ->
+ <<N:64/unsigned-native>> = <<Value:64/signed-native>>,
+ e_varint(N, Bin).
+
+-compile({nowarn_unused_function,e_type_bool/3}).
+e_type_bool(true, Bin, _TrUserData) ->
+ <<Bin/binary, 1>>;
+e_type_bool(false, Bin, _TrUserData) ->
+ <<Bin/binary, 0>>;
+e_type_bool(1, Bin, _TrUserData) -> <<Bin/binary, 1>>;
+e_type_bool(0, Bin, _TrUserData) -> <<Bin/binary, 0>>.
+
+-compile({nowarn_unused_function,e_type_string/3}).
+e_type_string(S, Bin, _TrUserData) ->
+ Utf8 = unicode:characters_to_binary(S),
+ Bin2 = e_varint(byte_size(Utf8), Bin),
+ <<Bin2/binary, Utf8/binary>>.
+
+-compile({nowarn_unused_function,e_type_bytes/3}).
+e_type_bytes(Bytes, Bin, _TrUserData)
+ when is_binary(Bytes) ->
+ Bin2 = e_varint(byte_size(Bytes), Bin),
+ <<Bin2/binary, Bytes/binary>>;
+e_type_bytes(Bytes, Bin, _TrUserData)
+ when is_list(Bytes) ->
+ BytesBin = iolist_to_binary(Bytes),
+ Bin2 = e_varint(byte_size(BytesBin), Bin),
+ <<Bin2/binary, BytesBin/binary>>.
+
+-compile({nowarn_unused_function,e_type_fixed32/3}).
+e_type_fixed32(Value, Bin, _TrUserData) ->
+ <<Bin/binary, Value:32/little>>.
+
+-compile({nowarn_unused_function,e_type_sfixed32/3}).
+e_type_sfixed32(Value, Bin, _TrUserData) ->
+ <<Bin/binary, Value:32/little-signed>>.
+
+-compile({nowarn_unused_function,e_type_fixed64/3}).
+e_type_fixed64(Value, Bin, _TrUserData) ->
+ <<Bin/binary, Value:64/little>>.
+
+-compile({nowarn_unused_function,e_type_sfixed64/3}).
+e_type_sfixed64(Value, Bin, _TrUserData) ->
+ <<Bin/binary, Value:64/little-signed>>.
+
+-compile({nowarn_unused_function,e_type_float/3}).
+e_type_float(V, Bin, _) when is_number(V) ->
+ <<Bin/binary, V:32/little-float>>;
+e_type_float(infinity, Bin, _) ->
+ <<Bin/binary, 0:16, 128, 127>>;
+e_type_float('-infinity', Bin, _) ->
+ <<Bin/binary, 0:16, 128, 255>>;
+e_type_float(nan, Bin, _) ->
+ <<Bin/binary, 0:16, 192, 127>>.
+
+-compile({nowarn_unused_function,e_type_double/3}).
+e_type_double(V, Bin, _) when is_number(V) ->
+ <<Bin/binary, V:64/little-float>>;
+e_type_double(infinity, Bin, _) ->
+ <<Bin/binary, 0:48, 240, 127>>;
+e_type_double('-infinity', Bin, _) ->
+ <<Bin/binary, 0:48, 240, 255>>;
+e_type_double(nan, Bin, _) ->
+ <<Bin/binary, 0:48, 248, 127>>.
+
+-compile({nowarn_unused_function,e_varint/3}).
+e_varint(N, Bin, _TrUserData) -> e_varint(N, Bin).
+
+-compile({nowarn_unused_function,e_varint/2}).
+e_varint(N, Bin) when N =< 127 -> <<Bin/binary, N>>;
+e_varint(N, Bin) ->
+ Bin2 = <<Bin/binary, (N band 127 bor 128)>>,
+ e_varint(N bsr 7, Bin2).
+
+
+decode_msg(Bin, MsgName) when is_binary(Bin) ->
+ decode_msg(Bin, MsgName, []).
+
+decode_msg(Bin, MsgName, Opts) when is_binary(Bin) ->
+ TrUserData = proplists:get_value(user_data, Opts),
+ decode_msg_1_catch(Bin, MsgName, TrUserData).
+
+-ifdef('OTP_RELEASE').
+decode_msg_1_catch(Bin, MsgName, TrUserData) ->
+ try decode_msg_2_doit(MsgName, Bin, TrUserData)
+ catch Class:Reason:StackTrace -> error({gpb_error,{decoding_failure, {Bin, MsgName, {Class, Reason, StackTrace}}}})
+ end.
+-else.
+-ifdef('GPB_PATTERN_STACK').
+decode_msg_1_catch(Bin, MsgName, TrUserData) ->
+ try decode_msg_2_doit(MsgName, Bin, TrUserData)
+ catch Class:Reason:StackTrace -> error({gpb_error,{decoding_failure, {Bin, MsgName, {Class, Reason, StackTrace}}}})
+ end.
+-else.
+decode_msg_1_catch(Bin, MsgName, TrUserData) ->
+ try decode_msg_2_doit(MsgName, Bin, TrUserData)
+ catch Class:Reason ->
+ StackTrace = erlang:get_stacktrace(),
+ error({gpb_error,{decoding_failure, {Bin, MsgName, {Class, Reason, StackTrace}}}})
+ end.
+-endif.
+
+-endif.
+
+decode_msg_2_doit('Versions', Bin, TrUserData) ->
+ id(d_msg_Versions(Bin, TrUserData), TrUserData);
+decode_msg_2_doit('Package', Bin, TrUserData) ->
+ id(d_msg_Package(Bin, TrUserData), TrUserData).
+
+
+
+d_msg_Versions(Bin, TrUserData) ->
+ dfp_read_field_def_Versions(Bin, 0, 0,
+ id([], TrUserData), id('$undef', TrUserData),
+ TrUserData).
+
+dfp_read_field_def_Versions(<<10, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, TrUserData) ->
+ d_field_Versions_packages(Rest, Z1, Z2, F@_1, F@_2,
+ TrUserData);
+dfp_read_field_def_Versions(<<18, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, TrUserData) ->
+ d_field_Versions_repository(Rest, Z1, Z2, F@_1, F@_2,
+ TrUserData);
+dfp_read_field_def_Versions(<<>>, 0, 0, R1, F@_2,
+ TrUserData) ->
+ S1 = #{repository => F@_2},
+ if R1 == '$undef' -> S1;
+ true -> S1#{packages => lists_reverse(R1, TrUserData)}
+ end;
+dfp_read_field_def_Versions(Other, Z1, Z2, F@_1, F@_2,
+ TrUserData) ->
+ dg_read_field_def_Versions(Other, Z1, Z2, F@_1, F@_2,
+ TrUserData).
+
+dg_read_field_def_Versions(<<1:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, TrUserData)
+ when N < 32 - 7 ->
+ dg_read_field_def_Versions(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, TrUserData);
+dg_read_field_def_Versions(<<0:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, TrUserData) ->
+ Key = X bsl N + Acc,
+ case Key of
+ 10 ->
+ d_field_Versions_packages(Rest, 0, 0, F@_1, F@_2,
+ TrUserData);
+ 18 ->
+ d_field_Versions_repository(Rest, 0, 0, F@_1, F@_2,
+ TrUserData);
+ _ ->
+ case Key band 7 of
+ 0 ->
+ skip_varint_Versions(Rest, 0, 0, F@_1, F@_2,
+ TrUserData);
+ 1 ->
+ skip_64_Versions(Rest, 0, 0, F@_1, F@_2, TrUserData);
+ 2 ->
+ skip_length_delimited_Versions(Rest, 0, 0, F@_1, F@_2,
+ TrUserData);
+ 3 ->
+ skip_group_Versions(Rest, Key bsr 3, 0, F@_1, F@_2,
+ TrUserData);
+ 5 ->
+ skip_32_Versions(Rest, 0, 0, F@_1, F@_2, TrUserData)
+ end
+ end;
+dg_read_field_def_Versions(<<>>, 0, 0, R1, F@_2,
+ TrUserData) ->
+ S1 = #{repository => F@_2},
+ if R1 == '$undef' -> S1;
+ true -> S1#{packages => lists_reverse(R1, TrUserData)}
+ end.
+
+d_field_Versions_packages(<<1:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, TrUserData)
+ when N < 57 ->
+ d_field_Versions_packages(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, TrUserData);
+d_field_Versions_packages(<<0:1, X:7, Rest/binary>>, N,
+ Acc, Prev, F@_2, TrUserData) ->
+ {NewFValue, RestF} = begin
+ Len = X bsl N + Acc,
+ <<Bs:Len/binary, Rest2/binary>> = Rest,
+ {id(d_msg_Package(Bs, TrUserData), TrUserData),
+ Rest2}
+ end,
+ dfp_read_field_def_Versions(RestF, 0, 0,
+ cons(NewFValue, Prev, TrUserData), F@_2,
+ TrUserData).
+
+d_field_Versions_repository(<<1:1, X:7, Rest/binary>>,
+ N, Acc, F@_1, F@_2, TrUserData)
+ when N < 57 ->
+ d_field_Versions_repository(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, TrUserData);
+d_field_Versions_repository(<<0:1, X:7, Rest/binary>>,
+ N, Acc, F@_1, _, TrUserData) ->
+ {NewFValue, RestF} = begin
+ Len = X bsl N + Acc,
+ <<Bytes:Len/binary, Rest2/binary>> = Rest,
+ {id(binary:copy(Bytes), TrUserData), Rest2}
+ end,
+ dfp_read_field_def_Versions(RestF, 0, 0, F@_1,
+ NewFValue, TrUserData).
+
+skip_varint_Versions(<<1:1, _:7, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, TrUserData) ->
+ skip_varint_Versions(Rest, Z1, Z2, F@_1, F@_2,
+ TrUserData);
+skip_varint_Versions(<<0:1, _:7, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, TrUserData) ->
+ dfp_read_field_def_Versions(Rest, Z1, Z2, F@_1, F@_2,
+ TrUserData).
+
+skip_length_delimited_Versions(<<1:1, X:7,
+ Rest/binary>>,
+ N, Acc, F@_1, F@_2, TrUserData)
+ when N < 57 ->
+ skip_length_delimited_Versions(Rest, N + 7,
+ X bsl N + Acc, F@_1, F@_2, TrUserData);
+skip_length_delimited_Versions(<<0:1, X:7,
+ Rest/binary>>,
+ N, Acc, F@_1, F@_2, TrUserData) ->
+ Length = X bsl N + Acc,
+ <<_:Length/binary, Rest2/binary>> = Rest,
+ dfp_read_field_def_Versions(Rest2, 0, 0, F@_1, F@_2,
+ TrUserData).
+
+skip_group_Versions(Bin, FNum, Z2, F@_1, F@_2,
+ TrUserData) ->
+ {_, Rest} = read_group(Bin, FNum),
+ dfp_read_field_def_Versions(Rest, 0, Z2, F@_1, F@_2,
+ TrUserData).
+
+skip_32_Versions(<<_:32, Rest/binary>>, Z1, Z2, F@_1,
+ F@_2, TrUserData) ->
+ dfp_read_field_def_Versions(Rest, Z1, Z2, F@_1, F@_2,
+ TrUserData).
+
+skip_64_Versions(<<_:64, Rest/binary>>, Z1, Z2, F@_1,
+ F@_2, TrUserData) ->
+ dfp_read_field_def_Versions(Rest, Z1, Z2, F@_1, F@_2,
+ TrUserData).
+
+d_msg_Package(Bin, TrUserData) ->
+ dfp_read_field_def_Package(Bin, 0, 0,
+ id('$undef', TrUserData), id([], TrUserData),
+ id([], TrUserData), TrUserData).
+
+dfp_read_field_def_Package(<<10, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, F@_3, TrUserData) ->
+ d_field_Package_name(Rest, Z1, Z2, F@_1, F@_2, F@_3,
+ TrUserData);
+dfp_read_field_def_Package(<<18, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, F@_3, TrUserData) ->
+ d_field_Package_versions(Rest, Z1, Z2, F@_1, F@_2, F@_3,
+ TrUserData);
+dfp_read_field_def_Package(<<26, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, F@_3, TrUserData) ->
+ d_pfield_Package_retired(Rest, Z1, Z2, F@_1, F@_2, F@_3,
+ TrUserData);
+dfp_read_field_def_Package(<<24, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, F@_3, TrUserData) ->
+ d_field_Package_retired(Rest, Z1, Z2, F@_1, F@_2, F@_3,
+ TrUserData);
+dfp_read_field_def_Package(<<>>, 0, 0, F@_1, R1, R2,
+ TrUserData) ->
+ #{name => F@_1,
+ versions => lists_reverse(R1, TrUserData),
+ retired => lists_reverse(R2, TrUserData)};
+dfp_read_field_def_Package(Other, Z1, Z2, F@_1, F@_2,
+ F@_3, TrUserData) ->
+ dg_read_field_def_Package(Other, Z1, Z2, F@_1, F@_2,
+ F@_3, TrUserData).
+
+dg_read_field_def_Package(<<1:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, F@_3, TrUserData)
+ when N < 32 - 7 ->
+ dg_read_field_def_Package(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, F@_3, TrUserData);
+dg_read_field_def_Package(<<0:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, F@_3, TrUserData) ->
+ Key = X bsl N + Acc,
+ case Key of
+ 10 ->
+ d_field_Package_name(Rest, 0, 0, F@_1, F@_2, F@_3,
+ TrUserData);
+ 18 ->
+ d_field_Package_versions(Rest, 0, 0, F@_1, F@_2, F@_3,
+ TrUserData);
+ 26 ->
+ d_pfield_Package_retired(Rest, 0, 0, F@_1, F@_2, F@_3,
+ TrUserData);
+ 24 ->
+ d_field_Package_retired(Rest, 0, 0, F@_1, F@_2, F@_3,
+ TrUserData);
+ _ ->
+ case Key band 7 of
+ 0 ->
+ skip_varint_Package(Rest, 0, 0, F@_1, F@_2, F@_3,
+ TrUserData);
+ 1 ->
+ skip_64_Package(Rest, 0, 0, F@_1, F@_2, F@_3,
+ TrUserData);
+ 2 ->
+ skip_length_delimited_Package(Rest, 0, 0, F@_1, F@_2,
+ F@_3, TrUserData);
+ 3 ->
+ skip_group_Package(Rest, Key bsr 3, 0, F@_1, F@_2, F@_3,
+ TrUserData);
+ 5 ->
+ skip_32_Package(Rest, 0, 0, F@_1, F@_2, F@_3,
+ TrUserData)
+ end
+ end;
+dg_read_field_def_Package(<<>>, 0, 0, F@_1, R1, R2,
+ TrUserData) ->
+ #{name => F@_1,
+ versions => lists_reverse(R1, TrUserData),
+ retired => lists_reverse(R2, TrUserData)}.
+
+d_field_Package_name(<<1:1, X:7, Rest/binary>>, N, Acc,
+ F@_1, F@_2, F@_3, TrUserData)
+ when N < 57 ->
+ d_field_Package_name(Rest, N + 7, X bsl N + Acc, F@_1,
+ F@_2, F@_3, TrUserData);
+d_field_Package_name(<<0:1, X:7, Rest/binary>>, N, Acc,
+ _, F@_2, F@_3, TrUserData) ->
+ {NewFValue, RestF} = begin
+ Len = X bsl N + Acc,
+ <<Bytes:Len/binary, Rest2/binary>> = Rest,
+ {id(binary:copy(Bytes), TrUserData), Rest2}
+ end,
+ dfp_read_field_def_Package(RestF, 0, 0, NewFValue, F@_2,
+ F@_3, TrUserData).
+
+d_field_Package_versions(<<1:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, F@_3, TrUserData)
+ when N < 57 ->
+ d_field_Package_versions(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, F@_3, TrUserData);
+d_field_Package_versions(<<0:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, Prev, F@_3, TrUserData) ->
+ {NewFValue, RestF} = begin
+ Len = X bsl N + Acc,
+ <<Bytes:Len/binary, Rest2/binary>> = Rest,
+ {id(binary:copy(Bytes), TrUserData), Rest2}
+ end,
+ dfp_read_field_def_Package(RestF, 0, 0, F@_1,
+ cons(NewFValue, Prev, TrUserData), F@_3,
+ TrUserData).
+
+d_field_Package_retired(<<1:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, F@_3, TrUserData)
+ when N < 57 ->
+ d_field_Package_retired(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, F@_3, TrUserData);
+d_field_Package_retired(<<0:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, Prev, TrUserData) ->
+ {NewFValue, RestF} = {begin
+ <<Res:32/signed-native>> = <<(X bsl N +
+ Acc):32/unsigned-native>>,
+ id(Res, TrUserData)
+ end,
+ Rest},
+ dfp_read_field_def_Package(RestF, 0, 0, F@_1, F@_2,
+ cons(NewFValue, Prev, TrUserData), TrUserData).
+
+d_pfield_Package_retired(<<1:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, F@_3, TrUserData)
+ when N < 57 ->
+ d_pfield_Package_retired(Rest, N + 7, X bsl N + Acc,
+ F@_1, F@_2, F@_3, TrUserData);
+d_pfield_Package_retired(<<0:1, X:7, Rest/binary>>, N,
+ Acc, F@_1, F@_2, E, TrUserData) ->
+ Len = X bsl N + Acc,
+ <<PackedBytes:Len/binary, Rest2/binary>> = Rest,
+ NewSeq = d_packed_field_Package_retired(PackedBytes, 0,
+ 0, E, TrUserData),
+ dfp_read_field_def_Package(Rest2, 0, 0, F@_1, F@_2,
+ NewSeq, TrUserData).
+
+d_packed_field_Package_retired(<<1:1, X:7,
+ Rest/binary>>,
+ N, Acc, AccSeq, TrUserData)
+ when N < 57 ->
+ d_packed_field_Package_retired(Rest, N + 7,
+ X bsl N + Acc, AccSeq, TrUserData);
+d_packed_field_Package_retired(<<0:1, X:7,
+ Rest/binary>>,
+ N, Acc, AccSeq, TrUserData) ->
+ {NewFValue, RestF} = {begin
+ <<Res:32/signed-native>> = <<(X bsl N +
+ Acc):32/unsigned-native>>,
+ id(Res, TrUserData)
+ end,
+ Rest},
+ d_packed_field_Package_retired(RestF, 0, 0,
+ [NewFValue | AccSeq], TrUserData);
+d_packed_field_Package_retired(<<>>, 0, 0, AccSeq, _) ->
+ AccSeq.
+
+skip_varint_Package(<<1:1, _:7, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, F@_3, TrUserData) ->
+ skip_varint_Package(Rest, Z1, Z2, F@_1, F@_2, F@_3,
+ TrUserData);
+skip_varint_Package(<<0:1, _:7, Rest/binary>>, Z1, Z2,
+ F@_1, F@_2, F@_3, TrUserData) ->
+ dfp_read_field_def_Package(Rest, Z1, Z2, F@_1, F@_2,
+ F@_3, TrUserData).
+
+skip_length_delimited_Package(<<1:1, X:7, Rest/binary>>,
+ N, Acc, F@_1, F@_2, F@_3, TrUserData)
+ when N < 57 ->
+ skip_length_delimited_Package(Rest, N + 7,
+ X bsl N + Acc, F@_1, F@_2, F@_3, TrUserData);
+skip_length_delimited_Package(<<0:1, X:7, Rest/binary>>,
+ N, Acc, F@_1, F@_2, F@_3, TrUserData) ->
+ Length = X bsl N + Acc,
+ <<_:Length/binary, Rest2/binary>> = Rest,
+ dfp_read_field_def_Package(Rest2, 0, 0, F@_1, F@_2,
+ F@_3, TrUserData).
+
+skip_group_Package(Bin, FNum, Z2, F@_1, F@_2, F@_3,
+ TrUserData) ->
+ {_, Rest} = read_group(Bin, FNum),
+ dfp_read_field_def_Package(Rest, 0, Z2, F@_1, F@_2,
+ F@_3, TrUserData).
+
+skip_32_Package(<<_:32, Rest/binary>>, Z1, Z2, F@_1,
+ F@_2, F@_3, TrUserData) ->
+ dfp_read_field_def_Package(Rest, Z1, Z2, F@_1, F@_2,
+ F@_3, TrUserData).
+
+skip_64_Package(<<_:64, Rest/binary>>, Z1, Z2, F@_1,
+ F@_2, F@_3, TrUserData) ->
+ dfp_read_field_def_Package(Rest, Z1, Z2, F@_1, F@_2,
+ F@_3, TrUserData).
+
+read_group(Bin, FieldNum) ->
+ {NumBytes, EndTagLen} = read_gr_b(Bin, 0, 0, 0, 0, FieldNum),
+ <<Group:NumBytes/binary, _:EndTagLen/binary, Rest/binary>> = Bin,
+ {Group, Rest}.
+
+%% Like skipping over fields, but record the total length,
+%% Each field is <(FieldNum bsl 3) bor FieldType> ++ <FieldValue>
+%% Record the length because varints may be non-optimally encoded.
+%%
+%% Groups can be nested, but assume the same FieldNum cannot be nested
+%% because group field numbers are shared with the rest of the fields
+%% numbers. Thus we can search just for an group-end with the same
+%% field number.
+%%
+%% (The only time the same group field number could occur would
+%% be in a nested sub message, but then it would be inside a
+%% length-delimited entry, which we skip-read by length.)
+read_gr_b(<<1:1, X:7, Tl/binary>>, N, Acc, NumBytes, TagLen, FieldNum)
+ when N < (32-7) ->
+ read_gr_b(Tl, N+7, X bsl N + Acc, NumBytes, TagLen+1, FieldNum);
+read_gr_b(<<0:1, X:7, Tl/binary>>, N, Acc, NumBytes, TagLen,
+ FieldNum) ->
+ Key = X bsl N + Acc,
+ TagLen1 = TagLen + 1,
+ case {Key bsr 3, Key band 7} of
+ {FieldNum, 4} -> % 4 = group_end
+ {NumBytes, TagLen1};
+ {_, 0} -> % 0 = varint
+ read_gr_vi(Tl, 0, NumBytes + TagLen1, FieldNum);
+ {_, 1} -> % 1 = bits64
+ <<_:64, Tl2/binary>> = Tl,
+ read_gr_b(Tl2, 0, 0, NumBytes + TagLen1 + 8, 0, FieldNum);
+ {_, 2} -> % 2 = length_delimited
+ read_gr_ld(Tl, 0, 0, NumBytes + TagLen1, FieldNum);
+ {_, 3} -> % 3 = group_start
+ read_gr_b(Tl, 0, 0, NumBytes + TagLen1, 0, FieldNum);
+ {_, 4} -> % 4 = group_end
+ read_gr_b(Tl, 0, 0, NumBytes + TagLen1, 0, FieldNum);
+ {_, 5} -> % 5 = bits32
+ <<_:32, Tl2/binary>> = Tl,
+ read_gr_b(Tl2, 0, 0, NumBytes + TagLen1 + 4, 0, FieldNum)
+ end.
+
+read_gr_vi(<<1:1, _:7, Tl/binary>>, N, NumBytes, FieldNum)
+ when N < (64-7) ->
+ read_gr_vi(Tl, N+7, NumBytes+1, FieldNum);
+read_gr_vi(<<0:1, _:7, Tl/binary>>, _, NumBytes, FieldNum) ->
+ read_gr_b(Tl, 0, 0, NumBytes+1, 0, FieldNum).
+
+read_gr_ld(<<1:1, X:7, Tl/binary>>, N, Acc, NumBytes, FieldNum)
+ when N < (64-7) ->
+ read_gr_ld(Tl, N+7, X bsl N + Acc, NumBytes+1, FieldNum);
+read_gr_ld(<<0:1, X:7, Tl/binary>>, N, Acc, NumBytes, FieldNum) ->
+ Len = X bsl N + Acc,
+ NumBytes1 = NumBytes + 1,
+ <<_:Len/binary, Tl2/binary>> = Tl,
+ read_gr_b(Tl2, 0, 0, NumBytes1 + Len, 0, FieldNum).
+
+merge_msgs(Prev, New, MsgName) when is_atom(MsgName) ->
+ merge_msgs(Prev, New, MsgName, []).
+
+merge_msgs(Prev, New, MsgName, Opts) ->
+ TrUserData = proplists:get_value(user_data, Opts),
+ case MsgName of
+ 'Versions' -> merge_msg_Versions(Prev, New, TrUserData);
+ 'Package' -> merge_msg_Package(Prev, New, TrUserData)
+ end.
+
+-compile({nowarn_unused_function,merge_msg_Versions/3}).
+merge_msg_Versions(#{} = PMsg,
+ #{repository := NFrepository} = NMsg, TrUserData) ->
+ S1 = #{repository => NFrepository},
+ case {PMsg, NMsg} of
+ {#{packages := PFpackages},
+ #{packages := NFpackages}} ->
+ S1#{packages =>
+ 'erlang_++'(PFpackages, NFpackages, TrUserData)};
+ {_, #{packages := NFpackages}} ->
+ S1#{packages => NFpackages};
+ {#{packages := PFpackages}, _} ->
+ S1#{packages => PFpackages};
+ {_, _} -> S1
+ end.
+
+-compile({nowarn_unused_function,merge_msg_Package/3}).
+merge_msg_Package(#{} = PMsg, #{name := NFname} = NMsg,
+ TrUserData) ->
+ S1 = #{name => NFname},
+ S2 = case {PMsg, NMsg} of
+ {#{versions := PFversions},
+ #{versions := NFversions}} ->
+ S1#{versions =>
+ 'erlang_++'(PFversions, NFversions, TrUserData)};
+ {_, #{versions := NFversions}} ->
+ S1#{versions => NFversions};
+ {#{versions := PFversions}, _} ->
+ S1#{versions => PFversions};
+ {_, _} -> S1
+ end,
+ case {PMsg, NMsg} of
+ {#{retired := PFretired}, #{retired := NFretired}} ->
+ S2#{retired =>
+ 'erlang_++'(PFretired, NFretired, TrUserData)};
+ {_, #{retired := NFretired}} ->
+ S2#{retired => NFretired};
+ {#{retired := PFretired}, _} ->
+ S2#{retired => PFretired};
+ {_, _} -> S2
+ end.
+
+
+verify_msg(Msg, MsgName) when is_atom(MsgName) ->
+ verify_msg(Msg, MsgName, []).
+
+verify_msg(Msg, MsgName, Opts) ->
+ TrUserData = proplists:get_value(user_data, Opts),
+ case MsgName of
+ 'Versions' ->
+ v_msg_Versions(Msg, [MsgName], TrUserData);
+ 'Package' -> v_msg_Package(Msg, [MsgName], TrUserData);
+ _ -> mk_type_error(not_a_known_message, Msg, [])
+ end.
+
+
+-compile({nowarn_unused_function,v_msg_Versions/3}).
+v_msg_Versions(#{repository := F2} = M, Path,
+ TrUserData) ->
+ case M of
+ #{packages := F1} ->
+ if is_list(F1) ->
+ _ = [v_msg_Package(Elem, [packages | Path], TrUserData)
+ || Elem <- F1],
+ ok;
+ true ->
+ mk_type_error({invalid_list_of, {msg, 'Package'}}, F1,
+ [packages | Path])
+ end;
+ _ -> ok
+ end,
+ v_type_string(F2, [repository | Path], TrUserData),
+ lists:foreach(fun (packages) -> ok;
+ (repository) -> ok;
+ (OtherKey) ->
+ mk_type_error({extraneous_key, OtherKey}, M, Path)
+ end,
+ maps:keys(M)),
+ ok;
+v_msg_Versions(M, Path, _TrUserData) when is_map(M) ->
+ mk_type_error({missing_fields,
+ [repository] -- maps:keys(M), 'Versions'},
+ M, Path);
+v_msg_Versions(X, Path, _TrUserData) ->
+ mk_type_error({expected_msg, 'Versions'}, X, Path).
+
+-compile({nowarn_unused_function,v_msg_Package/3}).
+v_msg_Package(#{name := F1} = M, Path, TrUserData) ->
+ v_type_string(F1, [name | Path], TrUserData),
+ case M of
+ #{versions := F2} ->
+ if is_list(F2) ->
+ _ = [v_type_string(Elem, [versions | Path], TrUserData)
+ || Elem <- F2],
+ ok;
+ true ->
+ mk_type_error({invalid_list_of, string}, F2,
+ [versions | Path])
+ end;
+ _ -> ok
+ end,
+ case M of
+ #{retired := F3} ->
+ if is_list(F3) ->
+ _ = [v_type_int32(Elem, [retired | Path], TrUserData)
+ || Elem <- F3],
+ ok;
+ true ->
+ mk_type_error({invalid_list_of, int32}, F3,
+ [retired | Path])
+ end;
+ _ -> ok
+ end,
+ lists:foreach(fun (name) -> ok;
+ (versions) -> ok;
+ (retired) -> ok;
+ (OtherKey) ->
+ mk_type_error({extraneous_key, OtherKey}, M, Path)
+ end,
+ maps:keys(M)),
+ ok;
+v_msg_Package(M, Path, _TrUserData) when is_map(M) ->
+ mk_type_error({missing_fields, [name] -- maps:keys(M),
+ 'Package'},
+ M, Path);
+v_msg_Package(X, Path, _TrUserData) ->
+ mk_type_error({expected_msg, 'Package'}, X, Path).
+
+-compile({nowarn_unused_function,v_type_int32/3}).
+v_type_int32(N, _Path, _TrUserData)
+ when -2147483648 =< N, N =< 2147483647 ->
+ ok;
+v_type_int32(N, Path, _TrUserData) when is_integer(N) ->
+ mk_type_error({value_out_of_range, int32, signed, 32},
+ N, Path);
+v_type_int32(X, Path, _TrUserData) ->
+ mk_type_error({bad_integer, int32, signed, 32}, X,
+ Path).
+
+-compile({nowarn_unused_function,v_type_string/3}).
+v_type_string(S, Path, _TrUserData)
+ when is_list(S); is_binary(S) ->
+ try unicode:characters_to_binary(S) of
+ B when is_binary(B) -> ok;
+ {error, _, _} ->
+ mk_type_error(bad_unicode_string, S, Path)
+ catch
+ error:badarg ->
+ mk_type_error(bad_unicode_string, S, Path)
+ end;
+v_type_string(X, Path, _TrUserData) ->
+ mk_type_error(bad_unicode_string, X, Path).
+
+-compile({nowarn_unused_function,mk_type_error/3}).
+-spec mk_type_error(_, _, list()) -> no_return().
+mk_type_error(Error, ValueSeen, Path) ->
+ Path2 = prettify_path(Path),
+ erlang:error({gpb_type_error,
+ {Error, [{value, ValueSeen}, {path, Path2}]}}).
+
+
+-compile({nowarn_unused_function,prettify_path/1}).
+prettify_path([]) -> top_level;
+prettify_path(PathR) ->
+ list_to_atom(string:join(lists:map(fun atom_to_list/1,
+ lists:reverse(PathR)),
+ ".")).
+
+
+-compile({nowarn_unused_function,id/2}).
+-compile({inline,id/2}).
+id(X, _TrUserData) -> X.
+
+-compile({nowarn_unused_function,v_ok/3}).
+-compile({inline,v_ok/3}).
+v_ok(_Value, _Path, _TrUserData) -> ok.
+
+-compile({nowarn_unused_function,m_overwrite/3}).
+-compile({inline,m_overwrite/3}).
+m_overwrite(_Prev, New, _TrUserData) -> New.
+
+-compile({nowarn_unused_function,cons/3}).
+-compile({inline,cons/3}).
+cons(Elem, Acc, _TrUserData) -> [Elem | Acc].
+
+-compile({nowarn_unused_function,lists_reverse/2}).
+-compile({inline,lists_reverse/2}).
+'lists_reverse'(L, _TrUserData) -> lists:reverse(L).
+-compile({nowarn_unused_function,'erlang_++'/3}).
+-compile({inline,'erlang_++'/3}).
+'erlang_++'(A, B, _TrUserData) -> A ++ B.
+
+get_msg_defs() ->
+ [{{msg, 'Versions'},
+ [#{name => packages, fnum => 1, rnum => 2,
+ type => {msg, 'Package'}, occurrence => repeated,
+ opts => []},
+ #{name => repository, fnum => 2, rnum => 3,
+ type => string, occurrence => required, opts => []}]},
+ {{msg, 'Package'},
+ [#{name => name, fnum => 1, rnum => 2, type => string,
+ occurrence => required, opts => []},
+ #{name => versions, fnum => 2, rnum => 3,
+ type => string, occurrence => repeated, opts => []},
+ #{name => retired, fnum => 3, rnum => 4, type => int32,
+ occurrence => repeated, opts => [packed]}]}].
+
+
+get_msg_names() -> ['Versions', 'Package'].
+
+
+get_group_names() -> [].
+
+
+get_msg_or_group_names() -> ['Versions', 'Package'].
+
+
+get_enum_names() -> [].
+
+
+fetch_msg_def(MsgName) ->
+ case find_msg_def(MsgName) of
+ Fs when is_list(Fs) -> Fs;
+ error -> erlang:error({no_such_msg, MsgName})
+ end.
+
+
+-spec fetch_enum_def(_) -> no_return().
+fetch_enum_def(EnumName) ->
+ erlang:error({no_such_enum, EnumName}).
+
+
+find_msg_def('Versions') ->
+ [#{name => packages, fnum => 1, rnum => 2,
+ type => {msg, 'Package'}, occurrence => repeated,
+ opts => []},
+ #{name => repository, fnum => 2, rnum => 3,
+ type => string, occurrence => required, opts => []}];
+find_msg_def('Package') ->
+ [#{name => name, fnum => 1, rnum => 2, type => string,
+ occurrence => required, opts => []},
+ #{name => versions, fnum => 2, rnum => 3,
+ type => string, occurrence => repeated, opts => []},
+ #{name => retired, fnum => 3, rnum => 4, type => int32,
+ occurrence => repeated, opts => [packed]}];
+find_msg_def(_) -> error.
+
+
+find_enum_def(_) -> error.
+
+
+-spec enum_symbol_by_value(_, _) -> no_return().
+enum_symbol_by_value(E, V) ->
+ erlang:error({no_enum_defs, E, V}).
+
+
+-spec enum_value_by_symbol(_, _) -> no_return().
+enum_value_by_symbol(E, V) ->
+ erlang:error({no_enum_defs, E, V}).
+
+
+
+get_service_names() -> [].
+
+
+get_service_def(_) -> error.
+
+
+get_rpc_names(_) -> error.
+
+
+find_rpc_def(_, _) -> error.
+
+
+
+-spec fetch_rpc_def(_, _) -> no_return().
+fetch_rpc_def(ServiceName, RpcName) ->
+ erlang:error({no_such_rpc, ServiceName, RpcName}).
+
+
+get_package_name() -> undefined.
+
+
+
+gpb_version_as_string() ->
+ "4.3.1".
+
+gpb_version_as_list() ->
+ [4,3,1].
diff --git a/src/r3_hex_registry.erl b/src/r3_hex_registry.erl
new file mode 100644
index 0000000..86f9176
--- /dev/null
+++ b/src/r3_hex_registry.erl
@@ -0,0 +1,133 @@
+%% Vendored from hex_core v0.5.0, do not edit manually
+
+-module(r3_hex_registry).
+-export([
+ encode_names/1,
+ decode_names/2,
+ encode_versions/1,
+ decode_versions/2,
+ encode_package/1,
+ decode_package/3,
+ sign_protobuf/2,
+ decode_signed/1,
+ decode_and_verify_signed/2,
+ sign/2,
+ verify/3
+]).
+-include_lib("public_key/include/public_key.hrl").
+
+-type private_key() :: public_key:rsa_private_key() | binary().
+-type public_key() :: public_key:rsa_public_key() | binary().
+
+%%====================================================================
+%% API functions
+%%====================================================================
+
+%% @doc
+%% Encode Names message.
+encode_names(Names) ->
+ r3_hex_pb_names:encode_msg(Names, 'Names').
+
+%% @doc
+%% Decode message created with encode_names/1.
+decode_names(Payload, no_verify) ->
+ #{packages := Packages} = r3_hex_pb_names:decode_msg(Payload, 'Names'),
+ {ok, Packages};
+
+decode_names(Payload, Repository) ->
+ case r3_hex_pb_names:decode_msg(Payload, 'Names') of
+ #{repository := Repository, packages := Packages} ->
+ {ok, Packages};
+ _ ->
+ {error, unverified}
+ end.
+
+%% @doc
+%% Encode Versions message.
+encode_versions(Versions) ->
+ r3_hex_pb_versions:encode_msg(Versions, 'Versions').
+
+%% @doc
+%% Decode message created with encode_versions/1.
+decode_versions(Payload, no_verify) ->
+ #{packages := Packages} = r3_hex_pb_versions:decode_msg(Payload, 'Versions'),
+ {ok, Packages};
+
+decode_versions(Payload, Repository) ->
+ case r3_hex_pb_versions:decode_msg(Payload, 'Versions') of
+ #{repository := Repository, packages := Packages} ->
+ {ok, Packages};
+ _ ->
+ {error, unverified}
+ end.
+
+%% @doc
+%% Encode Package message.
+encode_package(Package) ->
+ r3_hex_pb_package:encode_msg(Package, 'Package').
+
+%% @doc
+%% Decode message created with encode_package/1.
+decode_package(Payload, no_verify, no_verify) ->
+ #{releases := Releases} = r3_hex_pb_package:decode_msg(Payload, 'Package'),
+ {ok, Releases};
+
+decode_package(Payload, Repository, Package) ->
+ case r3_hex_pb_package:decode_msg(Payload, 'Package') of
+ #{repository := Repository, name := Package, releases := Releases} ->
+ {ok, Releases};
+ _ ->
+ {error, unverified}
+ end.
+
+%% @doc
+%% Encode Signed message.
+sign_protobuf(Payload, PrivateKey) ->
+ Signature = sign(Payload, PrivateKey),
+ r3_hex_pb_signed:encode_msg(#{payload => Payload, signature => Signature}, 'Signed').
+
+%% @doc
+%% Decode message created with sign_protobuf/2 without verification.
+decode_signed(Signed) ->
+ r3_hex_pb_signed:decode_msg(Signed, 'Signed').
+
+%% @doc
+%% Decode message created with sign_protobuf/2 and verify it against public key.
+-spec decode_and_verify_signed(map(), public_key()) -> {ok, binary()} | {error, term()}.
+decode_and_verify_signed(Signed, PublicKey) ->
+ #{payload := Payload, signature := Signature} = decode_signed(Signed),
+ case verify(Payload, Signature, PublicKey) of
+ true -> {ok, Payload};
+ false -> {error, unverified};
+ {error, Reason} -> {error, Reason}
+ end.
+
+%% @doc
+%% Signs binary with given private key.
+-spec sign(binary(), private_key()) -> binary().
+sign(Binary, PrivateKey) ->
+ {ok, RSAPrivateKey} = key(PrivateKey),
+ public_key:sign(Binary, sha512, RSAPrivateKey).
+
+%% @doc
+%% Verifies binary against signature and a public key.
+-spec verify(binary(), binary(), public_key()) -> boolean() | {error, term()}.
+verify(Binary, Signature, PublicKey) ->
+ case key(PublicKey) of
+ {ok, RSAPublicKey} -> public_key:verify(Binary, sha512, Signature, RSAPublicKey);
+ {error, Reason} -> {error, Reason}
+ end.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+
+key(#'RSAPublicKey'{} = Key) ->
+ {ok, Key};
+key(#'RSAPrivateKey'{} = Key) ->
+ {ok, Key};
+key(Binary) when is_binary(Binary) ->
+ case public_key:pem_decode(Binary) of
+ [Entry | _] -> {ok, public_key:pem_entry_decode(Entry)};
+ _ -> {error, bad_key}
+ end.
diff --git a/src/r3_hex_repo.erl b/src/r3_hex_repo.erl
new file mode 100644
index 0000000..dd8264f
--- /dev/null
+++ b/src/r3_hex_repo.erl
@@ -0,0 +1,174 @@
+%% Vendored from hex_core v0.5.0, do not edit manually
+
+-module(r3_hex_repo).
+-export([
+ get_names/1,
+ get_versions/1,
+ get_package/2,
+ get_tarball/3
+]).
+
+%%====================================================================
+%% API functions
+%%====================================================================
+
+%% @doc
+%% Gets names resource from the repository.
+%%
+%% Examples:
+%%
+%% ```
+%% > r3_hex_repo:get_names(r3_hex_core:default_config()).
+%% {ok, {200, ...,
+%% [
+%% #{name => <<"package1">>},
+%% #{name => <<"package2">>},
+%% ]}}
+%% '''
+%% @end
+get_names(Config) when is_map(Config) ->
+ Verify = maps:get(repo_verify_origin, Config, true),
+ Decoder = fun(Data) ->
+ case Verify of
+ true -> r3_hex_registry:decode_names(Data, repo_name(Config));
+ false -> r3_hex_registry:decode_names(Data, no_verify)
+ end
+ end,
+ get_protobuf(Config, <<"names">>, Decoder).
+
+%% @doc
+%% Gets versions resource from the repository.
+%%
+%% Examples:
+%%
+%% ```
+%% > r3_hex_repo:get_versions(Config).
+%% {ok, {200, ...,
+%% [
+%% #{name => <<"package1">>, retired => [],
+%% versions => [<<"1.0.0">>]},
+%% #{name => <<"package2">>, retired => [<<"0.5.0>>"],
+%% versions => [<<"0.5.0">>, <<"1.0.0">>]},
+%% ]}}
+%% '''
+%% @end
+get_versions(Config) when is_map(Config) ->
+ Verify = maps:get(repo_verify_origin, Config, true),
+ Decoder = fun(Data) ->
+ case Verify of
+ true -> r3_hex_registry:decode_versions(Data, repo_name(Config));
+ false -> r3_hex_registry:decode_versions(Data, no_verify)
+ end
+ end,
+ get_protobuf(Config, <<"versions">>, Decoder).
+
+%% @doc
+%% Gets package resource from the repository.
+%%
+%% Examples:
+%%
+%% ```
+%% > r3_hex_repo:get_package(r3_hex_core:default_config(), <<"package1">>).
+%% {ok, {200, ...,
+%% {
+%% #{checksum => ..., version => <<"0.5.0">>, dependencies => []},
+%% #{checksum => ..., version => <<"1.0.0">>, dependencies => [
+%% #{package => <<"package2">>, optional => true, requirement => <<"~> 0.1">>}
+%% ]},
+%% ]}}
+%% '''
+%% @end
+get_package(Config, Name) when is_binary(Name) and is_map(Config) ->
+ Verify = maps:get(repo_verify_origin, Config, true),
+ Decoder = fun(Data) ->
+ case Verify of
+ true -> r3_hex_registry:decode_package(Data, repo_name(Config), Name);
+ false -> r3_hex_registry:decode_package(Data, no_verify, no_verify)
+ end
+ end,
+ get_protobuf(Config, <<"packages/", Name/binary>>, Decoder).
+
+%% @doc
+%% Gets tarball from the repository.
+%%
+%% Examples:
+%%
+%% ```
+%% > {ok, {200, _, Tarball}} = r3_hex_repo:get_tarball(<<"package1">>, <<"1.0.0">>, r3_hex_core:default_config()),
+%% > {ok, #{metadata := Metadata}} = r3_hex_tarball:unpack(Tarball, memory).
+%% '''
+%% @end
+get_tarball(Config, Name, Version) ->
+ ReqHeaders = make_headers(Config),
+
+ case get(Config, tarball_url(Config, Name, Version), ReqHeaders) of
+ {ok, {200, RespHeaders, Tarball}} ->
+ {ok, {200, RespHeaders, Tarball}};
+
+ Other ->
+ Other
+ end.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+
+get(Config, URI, Headers) ->
+ r3_hex_http:request(Config, get, URI, Headers, undefined).
+
+get_protobuf(Config, Path, Decoder) ->
+ PublicKey = maps:get(repo_public_key, Config),
+ ReqHeaders = make_headers(Config),
+
+ case get(Config, build_url(Config, Path), ReqHeaders) of
+ {ok, {200, RespHeaders, Compressed}} ->
+ Signed = zlib:gunzip(Compressed),
+ case decode(Signed, PublicKey, Decoder, Config) of
+ {ok, Decoded} ->
+ {ok, {200, RespHeaders, Decoded}};
+
+ {error, _} = Error ->
+ Error
+ end;
+
+ Other ->
+ Other
+ end.
+
+decode(Signed, PublicKey, Decoder, Config) ->
+ Verify = maps:get(repo_verify, Config, true),
+
+ case Verify of
+ true ->
+ case r3_hex_registry:decode_and_verify_signed(Signed, PublicKey) of
+ {ok, Payload} ->
+ Decoder(Payload);
+ Other ->
+ Other
+ end;
+ false ->
+ #{payload := Payload} = r3_hex_registry:decode_signed(Signed),
+ Decoder(Payload)
+ end.
+
+repo_name(#{repo_organization := Name}) when is_binary(Name) -> Name;
+repo_name(#{repo_name := Name}) when is_binary(Name) -> Name.
+
+tarball_url(Config, Name, Version) ->
+ Filename = tarball_filename(Name, Version),
+ build_url(Config, <<"tarballs/", Filename/binary>>).
+
+build_url(#{repo_url := URI, repo_organization := Org}, Path) when is_binary(Org) ->
+ <<URI/binary, "/repos/", Org/binary, "/", Path/binary>>;
+build_url(#{repo_url := URI, repo_organization := undefined}, Path) ->
+ <<URI/binary, "/", Path/binary>>.
+
+tarball_filename(Name, Version) ->
+ <<Name/binary, "-", Version/binary, ".tar">>.
+
+make_headers(Config) ->
+ maps:fold(fun set_header/3, #{}, Config).
+
+set_header(http_etag, ETag, Headers) when is_binary(ETag) -> maps:put(<<"if-none-match">>, ETag, Headers);
+set_header(repo_key, Token, Headers) when is_binary(Token) -> maps:put(<<"authorization">>, Token, Headers);
+set_header(_, _, Headers) -> Headers.
diff --git a/src/r3_hex_tarball.erl b/src/r3_hex_tarball.erl
new file mode 100644
index 0000000..dd2c77a
--- /dev/null
+++ b/src/r3_hex_tarball.erl
@@ -0,0 +1,507 @@
+%% Vendored from hex_core v0.5.0, do not edit manually
+
+-module(r3_hex_tarball).
+-export([create/2, create_docs/1, unpack/2, format_checksum/1, format_error/1]).
+-ifdef(TEST).
+-export([do_decode_metadata/1, gzip/1, normalize_requirements/1]).
+-endif.
+-define(VERSION, <<"3">>).
+-define(TARBALL_MAX_SIZE, 8 * 1024 * 1024).
+-define(TARBALL_MAX_UNCOMPRESSED_SIZE, 64 * 1024 * 1024).
+-define(BUILD_TOOL_FILES, [
+ {<<"mix.exs">>, <<"mix">>},
+ {<<"rebar.config">>, <<"rebar3">>},
+ {<<"rebar">>, <<"rebar3">>},
+ {<<"Makefile">>, <<"make">>},
+ {<<"Makefile.win">>, <<"make">>}
+]).
+-include_lib("kernel/include/file.hrl").
+
+-type checksum() :: binary().
+-type contents() :: #{filename() => binary()}.
+-type filename() :: string().
+-type files() :: [filename() | {filename(), filename()}] | contents().
+-type metadata() :: map().
+-type tarball() :: binary().
+
+%%====================================================================
+%% API functions
+%%====================================================================
+
+%% @doc
+%% Creates a package tarball.
+%%
+%% Examples:
+%%
+%% ```
+%% > Metadata = #{<<"name">> => <<"foo">>, <<"version">> => <<"1.0.0">>},
+%% > Files = [{"src/foo.erl", <<"-module(foo).">>}],
+%% > {ok, {Tarball, Checksum}} = r3_hex_tarball:create(Metadata, Files).
+%% > Tarball.
+%% <<86,69,...>>
+%% > Checksum.
+%% <<40,32,...>>
+%% '''
+%% @end
+-spec create(metadata(), files()) -> {ok, {tarball(), checksum()}}.
+create(Metadata, Files) ->
+ MetadataBinary = encode_metadata(Metadata),
+ ContentsTarball = create_memory_tarball(Files),
+ ContentsTarballCompressed = gzip(ContentsTarball),
+ Checksum = checksum(?VERSION, MetadataBinary, ContentsTarballCompressed),
+ ChecksumBase16 = encode_base16(Checksum),
+
+ OuterFiles = [
+ {"VERSION", ?VERSION},
+ {"CHECKSUM", ChecksumBase16},
+ {"metadata.config", MetadataBinary},
+ {"contents.tar.gz", ContentsTarballCompressed}
+ ],
+
+ Tarball = create_memory_tarball(OuterFiles),
+
+ UncompressedSize = byte_size(ContentsTarball),
+
+ case(byte_size(Tarball) > ?TARBALL_MAX_SIZE) or (UncompressedSize > ?TARBALL_MAX_UNCOMPRESSED_SIZE) of
+ true ->
+ {error, {tarball, too_big}};
+
+ false ->
+ {ok, {Tarball, Checksum}}
+ end.
+
+%% @doc
+%% Creates a docs tarball.
+%%
+%% Examples:
+%%
+%% ```
+%% > Files = [{"doc/index.html", <<"Docs">>}],
+%% > {ok, {Tarball, Checksum}} = r3_hex_tarball:create_docs(Files).
+%% > Tarball.
+%% %%=> <<86,69,...>>
+%% > Checksum.
+%% %%=> <<40,32,...>>
+%% '''
+%% @end
+-spec create_docs(files()) -> {ok, {tarball(), checksum()}}.
+create_docs(Files) ->
+ UncompressedTarball = create_memory_tarball(Files),
+ UncompressedSize = byte_size(UncompressedTarball),
+ Tarball = gzip(UncompressedTarball),
+ Checksum = checksum(Tarball),
+ Size = byte_size(Tarball),
+
+ case(Size > ?TARBALL_MAX_SIZE) or (UncompressedSize > ?TARBALL_MAX_UNCOMPRESSED_SIZE) of
+ true ->
+ {error, {tarball, too_big}};
+
+ false ->
+ {ok, {Tarball, Checksum}}
+ end.
+
+%% @doc
+%% Unpacks a package tarball.
+%%
+%% Examples:
+%%
+%% ```
+%% > r3_hex_tarball:unpack(Tarball, memory).
+%% {ok,#{checksum => <<...>>,
+%% contents => [{"src/foo.erl",<<"-module(foo).">>}],
+%% metadata => #{<<"name">> => <<"foo">>, ...}}}
+%%
+%% > r3_hex_tarball:unpack(Tarball, "path/to/unpack").
+%% {ok,#{checksum => <<...>>,
+%% metadata => #{<<"name">> => <<"foo">>, ...}}}
+%% '''
+-spec unpack(tarball(), memory) ->
+ {ok, #{checksum => checksum(), metadata => metadata(), contents => contents()}} |
+ {error, term()};
+ (tarball(), filename()) ->
+ {ok, #{checksum => checksum(), metadata => metadata()}} |
+ {error, term()}.
+unpack(Tarball, _) when byte_size(Tarball) > ?TARBALL_MAX_SIZE ->
+ {error, {tarball, too_big}};
+
+unpack(Tarball, Output) ->
+ case r3_hex_erl_tar:extract({binary, Tarball}, [memory]) of
+ {ok, []} ->
+ {error, {tarball, empty}};
+
+ {ok, FileList} ->
+ do_unpack(maps:from_list(FileList), Output);
+
+ {error, Reason} ->
+ {error, {tarball, Reason}}
+ end.
+
+%% @doc
+%% Returns base16-encoded representation of checksum.
+-spec format_checksum(checksum()) -> binary().
+format_checksum(Checksum) ->
+ encode_base16(Checksum).
+
+%% @doc
+%% Converts an error reason term to a human-readable error message string.
+-spec format_error(term()) -> string().
+format_error({tarball, empty}) -> "empty tarball";
+format_error({tarball, too_big}) -> "tarball is too big";
+format_error({tarball, {missing_files, Files}}) -> io_lib:format("missing files: ~p", [Files]);
+format_error({tarball, {invalid_files, Files}}) -> io_lib:format("invalid files: ~p", [Files]);
+format_error({tarball, {bad_version, Vsn}}) -> io_lib:format("unsupported version: ~p", [Vsn]);
+format_error({tarball, invalid_checksum}) -> "invalid tarball checksum";
+format_error({tarball, Reason}) -> "tarball error, " ++ r3_hex_erl_tar:format_error(Reason);
+format_error({inner_tarball, Reason}) -> "inner tarball error, " ++ r3_hex_erl_tar:format_error(Reason);
+format_error({metadata, invalid_terms}) -> "error reading package metadata: invalid terms";
+format_error({metadata, not_key_value}) -> "error reading package metadata: not in key-value format";
+format_error({metadata, Reason}) -> "error reading package metadata" ++ r3_safe_erl_term:format_error(Reason);
+
+format_error({checksum_mismatch, ExpectedChecksum, ActualChecksum}) ->
+ io_lib:format(
+ "tarball checksum mismatch~n~n" ++
+ "Expected (base16-encoded): ~s~n" ++
+ "Actual (base16-encoded): ~s",
+ [encode_base16(ExpectedChecksum), encode_base16(ActualChecksum)]).
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+
+checksum(Version, MetadataBinary, ContentsBinary) ->
+ Blob = <<Version/binary, MetadataBinary/binary, ContentsBinary/binary>>,
+ crypto:hash(sha256, Blob).
+
+checksum(ContentsBinary) ->
+ Blob = <<ContentsBinary/binary>>,
+ crypto:hash(sha256, Blob).
+
+encode_metadata(Meta) ->
+ Data = lists:map(
+ fun(MetaPair) ->
+ String = io_lib_pretty:print(binarify(MetaPair), [{encoding, utf8}]),
+ unicode:characters_to_binary([String, ".\n"])
+ end, maps:to_list(Meta)),
+ iolist_to_binary(Data).
+
+do_unpack(Files, Output) ->
+ State = #{
+ checksum => undefined,
+ contents => undefined,
+ files => Files,
+ metadata => undefined,
+ output => Output
+ },
+ State1 = check_files(State),
+ State2 = check_version(State1),
+ State3 = check_checksum(State2),
+ State4 = decode_metadata(State3),
+ finish_unpack(State4).
+
+finish_unpack({error, _} = Error) ->
+ Error;
+finish_unpack(#{metadata := Metadata, files := Files, output := Output}) ->
+ _Version = maps:get("VERSION", Files),
+ Checksum = decode_base16(maps:get("CHECKSUM", Files)),
+ ContentsBinary = maps:get("contents.tar.gz", Files),
+ case unpack_tarball(ContentsBinary, Output) of
+ ok ->
+ copy_metadata_config(Output, maps:get("metadata.config", Files)),
+ {ok, #{checksum => Checksum, metadata => Metadata}};
+
+ {ok, Contents} ->
+ {ok, #{checksum => Checksum, metadata => Metadata, contents => Contents}};
+
+ {error, Reason} ->
+ {error, {inner_tarball, Reason}}
+ end.
+
+copy_metadata_config(Output, MetadataBinary) ->
+ ok = file:write_file(filename:join(Output, "hex_metadata.config"), MetadataBinary).
+
+check_files(#{files := Files} = State) ->
+ RequiredFiles = ["VERSION", "CHECKSUM", "metadata.config", "contents.tar.gz"],
+ case diff_keys(Files, RequiredFiles, []) of
+ ok ->
+ State;
+
+ {error, {missing_keys, Keys}} ->
+ {error, {tarball, {missing_files, Keys}}};
+
+ {error, {unknown_keys, Keys}} ->
+ {error, {tarball, {invalid_files, Keys}}}
+ end.
+
+check_version({error, _} = Error) ->
+ Error;
+check_version(#{files := Files} = State) ->
+ case maps:get("VERSION", Files) of
+ <<"3">> ->
+ State;
+
+ Version ->
+ {error, {tarball, {bad_version, Version}}}
+ end.
+
+check_checksum({error, _} = Error) ->
+ Error;
+check_checksum(#{files := Files} = State) ->
+ ChecksumBase16 = maps:get("CHECKSUM", Files),
+ ExpectedChecksum = decode_base16(ChecksumBase16),
+
+ Version = maps:get("VERSION", Files),
+ MetadataBinary = maps:get("metadata.config", Files),
+ ContentsBinary = maps:get("contents.tar.gz", Files),
+ ActualChecksum = checksum(Version, MetadataBinary, ContentsBinary),
+
+ if
+ byte_size(ExpectedChecksum) /= 32 ->
+ {error, {tarball, invalid_checksum}};
+
+ ExpectedChecksum == ActualChecksum ->
+ maps:put(checksum, ExpectedChecksum, State);
+
+ true ->
+ {error, {tarball, {checksum_mismatch, ExpectedChecksum, ActualChecksum}}}
+ end.
+
+decode_metadata({error, _} = Error) ->
+ Error;
+decode_metadata(#{files := #{"metadata.config" := Binary}} = State) when is_binary(Binary) ->
+ case do_decode_metadata(Binary) of
+ #{} = Metadata -> maps:put(metadata, normalize_metadata(Metadata), State);
+ Other -> Other
+ end.
+
+do_decode_metadata(Binary) when is_binary(Binary) ->
+ {ok, String} = characters_to_list(Binary),
+
+ case r3_safe_erl_term:string(String) of
+ {ok, Tokens, _Line} ->
+ try
+ Terms = r3_safe_erl_term:terms(Tokens),
+ maps:from_list(Terms)
+ catch
+ error:function_clause ->
+ {error, {metadata, invalid_terms}};
+
+ error:badarg ->
+ {error, {metadata, not_key_value}}
+ end;
+
+ {error, {_Line, r3_safe_erl_term, Reason}, _Line2} ->
+ {error, {metadata, Reason}}
+ end.
+
+characters_to_list(Binary) ->
+ case unicode:characters_to_list(Binary) of
+ List when is_list(List) ->
+ {ok, List};
+ {error, _, _} ->
+ case unicode:characters_to_list(Binary, latin1) of
+ List when is_list(List) -> {ok, List};
+ Other -> Other
+ end
+ end.
+
+normalize_metadata(Metadata1) ->
+ Metadata2 = maybe_update_with(<<"requirements">>, fun normalize_requirements/1, Metadata1),
+ Metadata3 = maybe_update_with(<<"links">>, fun try_into_map/1, Metadata2),
+ Metadata4 = maybe_update_with(<<"extra">>, fun try_into_map/1, Metadata3),
+ guess_build_tools(Metadata4).
+
+normalize_requirements(Requirements) ->
+ case is_list(Requirements) andalso (Requirements /= []) andalso is_list(hd(Requirements)) of
+ true ->
+ maps:from_list(lists:map(fun normalize_legacy_requirement/1, Requirements));
+
+ false ->
+ try_into_map(fun normalize_normal_requirement/1, Requirements)
+ end.
+
+normalize_normal_requirement({Name, Requirement}) ->
+ {Name, try_into_map(Requirement)}.
+
+normalize_legacy_requirement(Requirement) ->
+ Map = maps:from_list(Requirement),
+ Name = maps:get(<<"name">>, Map),
+ {Name, maps:without([<<"name">>], Map)}.
+
+guess_build_tools(#{<<"build_tools">> := BuildTools} = Metadata) when is_list(BuildTools) ->
+ Metadata;
+guess_build_tools(#{<<"files">> := Filenames} = Metadata) ->
+ BaseFiles = [Filename || Filename <- Filenames, filename:dirname(binary_to_list(Filename)) == "."],
+ BuildTools = lists:usort([Tool || {Filename, Tool} <- ?BUILD_TOOL_FILES, lists:member(Filename, BaseFiles)]),
+ Metadata#{<<"build_tools">> => BuildTools};
+guess_build_tools(Metadata) ->
+ Metadata.
+
+%%====================================================================
+%% Tar Helpers
+%%====================================================================
+
+unpack_tarball(ContentsBinary, memory) ->
+ r3_hex_erl_tar:extract({binary, ContentsBinary}, [memory, compressed]);
+unpack_tarball(ContentsBinary, Output) ->
+ case r3_hex_erl_tar:extract({binary, ContentsBinary}, [{cwd, Output}, compressed]) of
+ ok ->
+ [try_updating_mtime(filename:join(Output, Path)) || Path <- filelib:wildcard("**", Output)],
+ ok;
+ Other ->
+ Other
+ end.
+
+%% let it silently fail for bad symlinks
+try_updating_mtime(Path) ->
+ Time = calendar:universal_time(),
+ _ = file:write_file_info(Path, #file_info{mtime=Time}, [{time, universal}]),
+ ok.
+
+create_memory_tarball(Files) ->
+ Path = tmp_path(),
+ {ok, Tar} = r3_hex_erl_tar:open(Path, [write]),
+
+ try
+ add_files(Tar, Files)
+ after
+ ok = r3_hex_erl_tar:close(Tar)
+ end,
+ {ok, Tarball} = file:read_file(Path),
+ ok = file:delete(Path),
+ Tarball.
+
+tmp_path() ->
+ "tmp_" ++ binary_to_list(encode_base16(crypto:strong_rand_bytes(32))).
+
+add_files(Tar, Files) when is_list(Files) ->
+ lists:map(fun(File) -> add_file(Tar, File) end, Files).
+
+add_file(Tar, {Filename, Contents}) when is_list(Filename) and is_binary(Contents) ->
+ ok = r3_hex_erl_tar:add(Tar, Contents, Filename, tar_opts());
+add_file(Tar, Filename) when is_list(Filename) ->
+ add_file(Tar, {Filename, Filename});
+add_file(Tar, {Filename, AbsFilename}) when is_list(Filename), is_list(AbsFilename) ->
+ {ok, FileInfo} = file:read_link_info(AbsFilename, []),
+
+ case FileInfo#file_info.type of
+ symlink ->
+ ok = r3_hex_erl_tar:add(Tar, {Filename, AbsFilename}, tar_opts());
+ directory ->
+ case file:list_dir(AbsFilename) of
+ {ok, []} ->
+ r3_hex_erl_tar:add(Tar, {Filename, AbsFilename}, tar_opts());
+
+ {ok, _} ->
+ ok
+ end;
+ _ ->
+ Mode = FileInfo#file_info.mode,
+ {ok, Contents} = file:read_file(AbsFilename),
+ ok = r3_hex_erl_tar:add(Tar, Contents, Filename, Mode, tar_opts())
+ end.
+
+tar_opts() ->
+ NixEpoch = calendar:datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}}),
+ Y2kEpoch = calendar:datetime_to_gregorian_seconds({{2000, 1, 1}, {0, 0, 0}}),
+ Epoch = Y2kEpoch - NixEpoch,
+ [{atime, Epoch}, {mtime, Epoch}, {ctime, Epoch}, {uid, 0}, {gid, 0}].
+
+%% Reproducible gzip by not setting mtime and OS
+%%
+%% From https://tools.ietf.org/html/rfc1952
+%%
+%% +---+---+---+---+---+---+---+---+---+---+
+%% |ID1|ID2|CM |FLG| MTIME |XFL|OS | (more-->)
+%% +---+---+---+---+---+---+---+---+---+---+
+%%
+%% +=======================+
+%% |...compressed blocks...| (more-->)
+%% +=======================+
+%%
+%% +---+---+---+---+---+---+---+---+
+%% | CRC32 | ISIZE |
+%% +---+---+---+---+---+---+---+---+
+gzip(Uncompressed) ->
+ Compressed = gzip_no_header(Uncompressed),
+ Header = <<31, 139, 8, 0, 0, 0, 0, 0, 0, 0>>,
+ Crc = erlang:crc32(Uncompressed),
+ Size = byte_size(Uncompressed),
+ Trailer = <<Crc:32/little, Size:32/little>>,
+ iolist_to_binary([Header, Compressed, Trailer]).
+
+gzip_no_header(Uncompressed) ->
+ Zstream = zlib:open(),
+
+ try
+ zlib:deflateInit(Zstream, default, deflated, -15, 8, default),
+ Compressed = zlib:deflate(Zstream, Uncompressed, finish),
+ zlib:deflateEnd(Zstream),
+ iolist_to_binary(Compressed)
+ after
+ zlib:close(Zstream)
+ end.
+
+%%====================================================================
+%% Helpers
+%%====================================================================
+
+binarify(Binary) when is_binary(Binary) -> Binary;
+binarify(Number) when is_number(Number) -> Number;
+binarify(Atom) when Atom == undefined orelse is_boolean(Atom) -> Atom;
+binarify(Atom) when is_atom(Atom) -> atom_to_binary(Atom, utf8);
+binarify(List) when is_list(List) ->
+ [binarify(E) || E <- List];
+binarify({Key, Value}) ->
+ {binarify(Key), binarify(Value)};
+binarify(Map) when is_map(Map) ->
+ List = maps:to_list(Map),
+ lists:map(fun({K, V}) -> binarify({K, V}) end, List).
+
+diff_keys(Map, RequiredKeys, OptionalKeys) ->
+ Keys = maps:keys(Map),
+ MissingKeys = RequiredKeys -- Keys,
+ UnknownKeys = Keys -- (RequiredKeys ++ OptionalKeys),
+
+ case {MissingKeys, UnknownKeys} of
+ {[], []} ->
+ ok;
+
+ {_, [_ | _]} ->
+ {error, {unknown_keys, UnknownKeys}};
+
+ _ ->
+ {error, {missing_keys, MissingKeys}}
+ end.
+
+maybe_update_with(Key, Fun, Map) ->
+ case maps:find(Key, Map) of
+ {ok, Value} -> maps:put(Key, Fun(Value), Map);
+ error -> Map
+ end.
+
+try_into_map(List) ->
+ try_into_map(fun(X) -> X end, List).
+
+try_into_map(Fun, Input) ->
+ case is_list(Input) andalso lists:all(fun(E) -> is_tuple(E) andalso (tuple_size(E) == 2) end, Input) of
+ true -> maps:from_list(lists:map(Fun, Input));
+ false -> Input
+ end.
+
+encode_base16(Binary) ->
+ <<X:256/big-unsigned-integer>> = Binary,
+ String = string:to_upper(lists:flatten(io_lib:format("~64.16.0b", [X]))),
+ list_to_binary(String).
+
+%% Based on https://github.com/goj/base16/blob/master/src/base16.erl
+%% (C) 2012, Erlang Solutions Ltd.
+
+decode_base16(Base16) ->
+ << <<(unhex(H) bsl 4 + unhex(L))>> || <<H,L>> <= Base16 >>.
+
+unhex(D) when $0 =< D andalso D =< $9 ->
+ D - $0;
+unhex(D) when $a =< D andalso D =< $f ->
+ 10 + D - $a;
+unhex(D) when $A =< D andalso D =< $F ->
+ 10 + D - $A.
diff --git a/src/r3_safe_erl_term.erl b/src/r3_safe_erl_term.erl
new file mode 100644
index 0000000..34c306a
--- /dev/null
+++ b/src/r3_safe_erl_term.erl
@@ -0,0 +1,678 @@
+-file("/usr/local/lib/erlang/lib/parsetools-2.1.8/include/leexinc.hrl", 0).
+%% The source of this file is part of leex distribution, as such it
+%% has the same Copyright as the other files in the leex
+%% distribution. The Copyright is defined in the accompanying file
+%% COPYRIGHT. However, the resultant scanner generated by leex is the
+%% property of the creator of the scanner and is not covered by that
+%% Copyright.
+
+-module(r3_safe_erl_term).
+
+-export([string/1,string/2,token/2,token/3,tokens/2,tokens/3]).
+-export([format_error/1]).
+
+%% User code. This is placed here to allow extra attributes.
+-file("/Users/starbelly/devel/erlang/rebar3/src/hex_core/r3_safe_erl_term.xrl", 26).
+
+-export([terms/1]).
+
+terms(Tokens) ->
+ terms(Tokens, []).
+
+terms([{dot, _} = H], Buffer) ->
+ [buffer_to_term([H|Buffer])];
+terms([{dot, _} = H|T], Buffer) ->
+ [buffer_to_term([H|Buffer])|terms(T, [])];
+terms([H|T], Buffer) ->
+ terms(T, [H|Buffer]).
+
+buffer_to_term(Buffer) ->
+ {ok, Term} = erl_parse:parse_term(lists:reverse(Buffer)),
+ Term.
+
+unquote(TokenChars, TokenLen) ->
+ lists:sublist(TokenChars, 2, TokenLen - 2).
+
+tokenize_atom(TokenChars, TokenLine) ->
+ try list_to_existing_atom(TokenChars) of
+ Atom -> {token, {atom, TokenLine, Atom}}
+ catch
+ error:badarg -> {error, "illegal atom " ++ TokenChars}
+ end.
+
+escape([$\\|Cs]) ->
+ do_escape(Cs);
+escape([C|Cs]) ->
+ [C|escape(Cs)];
+escape([]) -> [].
+
+do_escape([O1,O2,O3|S]) when
+ O1 >= $0, O1 =< $7, O2 >= $0, O2 =< $7, O3 >= $0, O3 =< $7 ->
+ [(O1*8 + O2)*8 + O3 - 73*$0|escape(S)];
+do_escape([$^,C|Cs]) ->
+ [C band 31|escape(Cs)];
+do_escape([C|Cs]) when C >= $\000, C =< $\s ->
+ escape(Cs);
+do_escape([C|Cs]) ->
+ [escape_char(C)|escape(Cs)].
+
+escape_char($n) -> $\n; %\n = LF
+escape_char($r) -> $\r; %\r = CR
+escape_char($t) -> $\t; %\t = TAB
+escape_char($v) -> $\v; %\v = VT
+escape_char($b) -> $\b; %\b = BS
+escape_char($f) -> $\f; %\f = FF
+escape_char($e) -> $\e; %\e = ESC
+escape_char($s) -> $\s; %\s = SPC
+escape_char($d) -> $\d; %\d = DEL
+escape_char(C) -> C.
+
+-file("/usr/local/lib/erlang/lib/parsetools-2.1.8/include/leexinc.hrl", 14).
+
+format_error({illegal,S}) -> ["illegal characters ",io_lib:write_string(S)];
+format_error({user,S}) -> S.
+
+string(String) -> string(String, 1).
+
+string(String, Line) -> string(String, Line, String, []).
+
+%% string(InChars, Line, TokenChars, Tokens) ->
+%% {ok,Tokens,Line} | {error,ErrorInfo,Line}.
+%% Note the line number going into yystate, L0, is line of token
+%% start while line number returned is line of token end. We want line
+%% of token start.
+
+string([], L, [], Ts) -> % No partial tokens!
+ {ok,yyrev(Ts),L};
+string(Ics0, L0, Tcs, Ts) ->
+ case yystate(yystate(), Ics0, L0, 0, reject, 0) of
+ {A,Alen,Ics1,L1} -> % Accepting end state
+ string_cont(Ics1, L1, yyaction(A, Alen, Tcs, L0), Ts);
+ {A,Alen,Ics1,L1,_S1} -> % Accepting transistion state
+ string_cont(Ics1, L1, yyaction(A, Alen, Tcs, L0), Ts);
+ {reject,_Alen,Tlen,_Ics1,L1,_S1} -> % After a non-accepting state
+ {error,{L0,?MODULE,{illegal,yypre(Tcs, Tlen+1)}},L1};
+ {A,Alen,Tlen,_Ics1,L1,_S1} ->
+ Tcs1 = yysuf(Tcs, Alen),
+ L2 = adjust_line(Tlen, Alen, Tcs1, L1),
+ string_cont(Tcs1, L2, yyaction(A, Alen, Tcs, L0), Ts)
+ end.
+
+%% string_cont(RestChars, Line, Token, Tokens)
+%% Test for and remove the end token wrapper. Push back characters
+%% are prepended to RestChars.
+
+-dialyzer({nowarn_function, string_cont/4}).
+
+string_cont(Rest, Line, {token,T}, Ts) ->
+ string(Rest, Line, Rest, [T|Ts]);
+string_cont(Rest, Line, {token,T,Push}, Ts) ->
+ NewRest = Push ++ Rest,
+ string(NewRest, Line, NewRest, [T|Ts]);
+string_cont(Rest, Line, {end_token,T}, Ts) ->
+ string(Rest, Line, Rest, [T|Ts]);
+string_cont(Rest, Line, {end_token,T,Push}, Ts) ->
+ NewRest = Push ++ Rest,
+ string(NewRest, Line, NewRest, [T|Ts]);
+string_cont(Rest, Line, skip_token, Ts) ->
+ string(Rest, Line, Rest, Ts);
+string_cont(Rest, Line, {skip_token,Push}, Ts) ->
+ NewRest = Push ++ Rest,
+ string(NewRest, Line, NewRest, Ts);
+string_cont(_Rest, Line, {error,S}, _Ts) ->
+ {error,{Line,?MODULE,{user,S}},Line}.
+
+%% token(Continuation, Chars) ->
+%% token(Continuation, Chars, Line) ->
+%% {more,Continuation} | {done,ReturnVal,RestChars}.
+%% Must be careful when re-entering to append the latest characters to the
+%% after characters in an accept. The continuation is:
+%% {token,State,CurrLine,TokenChars,TokenLen,TokenLine,AccAction,AccLen}
+
+token(Cont, Chars) -> token(Cont, Chars, 1).
+
+token([], Chars, Line) ->
+ token(yystate(), Chars, Line, Chars, 0, Line, reject, 0);
+token({token,State,Line,Tcs,Tlen,Tline,Action,Alen}, Chars, _) ->
+ token(State, Chars, Line, Tcs ++ Chars, Tlen, Tline, Action, Alen).
+
+%% token(State, InChars, Line, TokenChars, TokenLen, TokenLine,
+%% AcceptAction, AcceptLen) ->
+%% {more,Continuation} | {done,ReturnVal,RestChars}.
+%% The argument order is chosen to be more efficient.
+
+token(S0, Ics0, L0, Tcs, Tlen0, Tline, A0, Alen0) ->
+ case yystate(S0, Ics0, L0, Tlen0, A0, Alen0) of
+ %% Accepting end state, we have a token.
+ {A1,Alen1,Ics1,L1} ->
+ token_cont(Ics1, L1, yyaction(A1, Alen1, Tcs, Tline));
+ %% Accepting transition state, can take more chars.
+ {A1,Alen1,[],L1,S1} -> % Need more chars to check
+ {more,{token,S1,L1,Tcs,Alen1,Tline,A1,Alen1}};
+ {A1,Alen1,Ics1,L1,_S1} -> % Take what we got
+ token_cont(Ics1, L1, yyaction(A1, Alen1, Tcs, Tline));
+ %% After a non-accepting state, maybe reach accept state later.
+ {A1,Alen1,Tlen1,[],L1,S1} -> % Need more chars to check
+ {more,{token,S1,L1,Tcs,Tlen1,Tline,A1,Alen1}};
+ {reject,_Alen1,Tlen1,eof,L1,_S1} -> % No token match
+ %% Check for partial token which is error.
+ Ret = if Tlen1 > 0 -> {error,{Tline,?MODULE,
+ %% Skip eof tail in Tcs.
+ {illegal,yypre(Tcs, Tlen1)}},L1};
+ true -> {eof,L1}
+ end,
+ {done,Ret,eof};
+ {reject,_Alen1,Tlen1,Ics1,L1,_S1} -> % No token match
+ Error = {Tline,?MODULE,{illegal,yypre(Tcs, Tlen1+1)}},
+ {done,{error,Error,L1},Ics1};
+ {A1,Alen1,Tlen1,_Ics1,L1,_S1} -> % Use last accept match
+ Tcs1 = yysuf(Tcs, Alen1),
+ L2 = adjust_line(Tlen1, Alen1, Tcs1, L1),
+ token_cont(Tcs1, L2, yyaction(A1, Alen1, Tcs, Tline))
+ end.
+
+%% token_cont(RestChars, Line, Token)
+%% If we have a token or error then return done, else if we have a
+%% skip_token then continue.
+
+-dialyzer({nowarn_function, token_cont/3}).
+
+token_cont(Rest, Line, {token,T}) ->
+ {done,{ok,T,Line},Rest};
+token_cont(Rest, Line, {token,T,Push}) ->
+ NewRest = Push ++ Rest,
+ {done,{ok,T,Line},NewRest};
+token_cont(Rest, Line, {end_token,T}) ->
+ {done,{ok,T,Line},Rest};
+token_cont(Rest, Line, {end_token,T,Push}) ->
+ NewRest = Push ++ Rest,
+ {done,{ok,T,Line},NewRest};
+token_cont(Rest, Line, skip_token) ->
+ token(yystate(), Rest, Line, Rest, 0, Line, reject, 0);
+token_cont(Rest, Line, {skip_token,Push}) ->
+ NewRest = Push ++ Rest,
+ token(yystate(), NewRest, Line, NewRest, 0, Line, reject, 0);
+token_cont(Rest, Line, {error,S}) ->
+ {done,{error,{Line,?MODULE,{user,S}},Line},Rest}.
+
+%% tokens(Continuation, Chars, Line) ->
+%% {more,Continuation} | {done,ReturnVal,RestChars}.
+%% Must be careful when re-entering to append the latest characters to the
+%% after characters in an accept. The continuation is:
+%% {tokens,State,CurrLine,TokenChars,TokenLen,TokenLine,Tokens,AccAction,AccLen}
+%% {skip_tokens,State,CurrLine,TokenChars,TokenLen,TokenLine,Error,AccAction,AccLen}
+
+tokens(Cont, Chars) -> tokens(Cont, Chars, 1).
+
+tokens([], Chars, Line) ->
+ tokens(yystate(), Chars, Line, Chars, 0, Line, [], reject, 0);
+tokens({tokens,State,Line,Tcs,Tlen,Tline,Ts,Action,Alen}, Chars, _) ->
+ tokens(State, Chars, Line, Tcs ++ Chars, Tlen, Tline, Ts, Action, Alen);
+tokens({skip_tokens,State,Line,Tcs,Tlen,Tline,Error,Action,Alen}, Chars, _) ->
+ skip_tokens(State, Chars, Line, Tcs ++ Chars, Tlen, Tline, Error, Action, Alen).
+
+%% tokens(State, InChars, Line, TokenChars, TokenLen, TokenLine, Tokens,
+%% AcceptAction, AcceptLen) ->
+%% {more,Continuation} | {done,ReturnVal,RestChars}.
+
+tokens(S0, Ics0, L0, Tcs, Tlen0, Tline, Ts, A0, Alen0) ->
+ case yystate(S0, Ics0, L0, Tlen0, A0, Alen0) of
+ %% Accepting end state, we have a token.
+ {A1,Alen1,Ics1,L1} ->
+ tokens_cont(Ics1, L1, yyaction(A1, Alen1, Tcs, Tline), Ts);
+ %% Accepting transition state, can take more chars.
+ {A1,Alen1,[],L1,S1} -> % Need more chars to check
+ {more,{tokens,S1,L1,Tcs,Alen1,Tline,Ts,A1,Alen1}};
+ {A1,Alen1,Ics1,L1,_S1} -> % Take what we got
+ tokens_cont(Ics1, L1, yyaction(A1, Alen1, Tcs, Tline), Ts);
+ %% After a non-accepting state, maybe reach accept state later.
+ {A1,Alen1,Tlen1,[],L1,S1} -> % Need more chars to check
+ {more,{tokens,S1,L1,Tcs,Tlen1,Tline,Ts,A1,Alen1}};
+ {reject,_Alen1,Tlen1,eof,L1,_S1} -> % No token match
+ %% Check for partial token which is error, no need to skip here.
+ Ret = if Tlen1 > 0 -> {error,{Tline,?MODULE,
+ %% Skip eof tail in Tcs.
+ {illegal,yypre(Tcs, Tlen1)}},L1};
+ Ts == [] -> {eof,L1};
+ true -> {ok,yyrev(Ts),L1}
+ end,
+ {done,Ret,eof};
+ {reject,_Alen1,Tlen1,_Ics1,L1,_S1} ->
+ %% Skip rest of tokens.
+ Error = {L1,?MODULE,{illegal,yypre(Tcs, Tlen1+1)}},
+ skip_tokens(yysuf(Tcs, Tlen1+1), L1, Error);
+ {A1,Alen1,Tlen1,_Ics1,L1,_S1} ->
+ Token = yyaction(A1, Alen1, Tcs, Tline),
+ Tcs1 = yysuf(Tcs, Alen1),
+ L2 = adjust_line(Tlen1, Alen1, Tcs1, L1),
+ tokens_cont(Tcs1, L2, Token, Ts)
+ end.
+
+%% tokens_cont(RestChars, Line, Token, Tokens)
+%% If we have an end_token or error then return done, else if we have
+%% a token then save it and continue, else if we have a skip_token
+%% just continue.
+
+-dialyzer({nowarn_function, tokens_cont/4}).
+
+tokens_cont(Rest, Line, {token,T}, Ts) ->
+ tokens(yystate(), Rest, Line, Rest, 0, Line, [T|Ts], reject, 0);
+tokens_cont(Rest, Line, {token,T,Push}, Ts) ->
+ NewRest = Push ++ Rest,
+ tokens(yystate(), NewRest, Line, NewRest, 0, Line, [T|Ts], reject, 0);
+tokens_cont(Rest, Line, {end_token,T}, Ts) ->
+ {done,{ok,yyrev(Ts, [T]),Line},Rest};
+tokens_cont(Rest, Line, {end_token,T,Push}, Ts) ->
+ NewRest = Push ++ Rest,
+ {done,{ok,yyrev(Ts, [T]),Line},NewRest};
+tokens_cont(Rest, Line, skip_token, Ts) ->
+ tokens(yystate(), Rest, Line, Rest, 0, Line, Ts, reject, 0);
+tokens_cont(Rest, Line, {skip_token,Push}, Ts) ->
+ NewRest = Push ++ Rest,
+ tokens(yystate(), NewRest, Line, NewRest, 0, Line, Ts, reject, 0);
+tokens_cont(Rest, Line, {error,S}, _Ts) ->
+ skip_tokens(Rest, Line, {Line,?MODULE,{user,S}}).
+
+%%skip_tokens(InChars, Line, Error) -> {done,{error,Error,Line},Ics}.
+%% Skip tokens until an end token, junk everything and return the error.
+
+skip_tokens(Ics, Line, Error) ->
+ skip_tokens(yystate(), Ics, Line, Ics, 0, Line, Error, reject, 0).
+
+%% skip_tokens(State, InChars, Line, TokenChars, TokenLen, TokenLine, Tokens,
+%% AcceptAction, AcceptLen) ->
+%% {more,Continuation} | {done,ReturnVal,RestChars}.
+
+skip_tokens(S0, Ics0, L0, Tcs, Tlen0, Tline, Error, A0, Alen0) ->
+ case yystate(S0, Ics0, L0, Tlen0, A0, Alen0) of
+ {A1,Alen1,Ics1,L1} -> % Accepting end state
+ skip_cont(Ics1, L1, yyaction(A1, Alen1, Tcs, Tline), Error);
+ {A1,Alen1,[],L1,S1} -> % After an accepting state
+ {more,{skip_tokens,S1,L1,Tcs,Alen1,Tline,Error,A1,Alen1}};
+ {A1,Alen1,Ics1,L1,_S1} ->
+ skip_cont(Ics1, L1, yyaction(A1, Alen1, Tcs, Tline), Error);
+ {A1,Alen1,Tlen1,[],L1,S1} -> % After a non-accepting state
+ {more,{skip_tokens,S1,L1,Tcs,Tlen1,Tline,Error,A1,Alen1}};
+ {reject,_Alen1,_Tlen1,eof,L1,_S1} ->
+ {done,{error,Error,L1},eof};
+ {reject,_Alen1,Tlen1,_Ics1,L1,_S1} ->
+ skip_tokens(yysuf(Tcs, Tlen1+1), L1, Error);
+ {A1,Alen1,Tlen1,_Ics1,L1,_S1} ->
+ Token = yyaction(A1, Alen1, Tcs, Tline),
+ Tcs1 = yysuf(Tcs, Alen1),
+ L2 = adjust_line(Tlen1, Alen1, Tcs1, L1),
+ skip_cont(Tcs1, L2, Token, Error)
+ end.
+
+%% skip_cont(RestChars, Line, Token, Error)
+%% Skip tokens until we have an end_token or error then return done
+%% with the original rror.
+
+-dialyzer({nowarn_function, skip_cont/4}).
+
+skip_cont(Rest, Line, {token,_T}, Error) ->
+ skip_tokens(yystate(), Rest, Line, Rest, 0, Line, Error, reject, 0);
+skip_cont(Rest, Line, {token,_T,Push}, Error) ->
+ NewRest = Push ++ Rest,
+ skip_tokens(yystate(), NewRest, Line, NewRest, 0, Line, Error, reject, 0);
+skip_cont(Rest, Line, {end_token,_T}, Error) ->
+ {done,{error,Error,Line},Rest};
+skip_cont(Rest, Line, {end_token,_T,Push}, Error) ->
+ NewRest = Push ++ Rest,
+ {done,{error,Error,Line},NewRest};
+skip_cont(Rest, Line, skip_token, Error) ->
+ skip_tokens(yystate(), Rest, Line, Rest, 0, Line, Error, reject, 0);
+skip_cont(Rest, Line, {skip_token,Push}, Error) ->
+ NewRest = Push ++ Rest,
+ skip_tokens(yystate(), NewRest, Line, NewRest, 0, Line, Error, reject, 0);
+skip_cont(Rest, Line, {error,_S}, Error) ->
+ skip_tokens(yystate(), Rest, Line, Rest, 0, Line, Error, reject, 0).
+
+-compile({nowarn_unused_function, [yyrev/1, yyrev/2, yypre/2, yysuf/2]}).
+
+yyrev(List) -> lists:reverse(List).
+yyrev(List, Tail) -> lists:reverse(List, Tail).
+yypre(List, N) -> lists:sublist(List, N).
+yysuf(List, N) -> lists:nthtail(N, List).
+
+%% adjust_line(TokenLength, AcceptLength, Chars, Line) -> NewLine
+%% Make sure that newlines in Chars are not counted twice.
+%% Line has been updated with respect to newlines in the prefix of
+%% Chars consisting of (TokenLength - AcceptLength) characters.
+
+-compile({nowarn_unused_function, adjust_line/4}).
+
+adjust_line(N, N, _Cs, L) -> L;
+adjust_line(T, A, [$\n|Cs], L) ->
+ adjust_line(T-1, A, Cs, L-1);
+adjust_line(T, A, [_|Cs], L) ->
+ adjust_line(T-1, A, Cs, L).
+
+%% yystate() -> InitialState.
+%% yystate(State, InChars, Line, CurrTokLen, AcceptAction, AcceptLen) ->
+%% {Action, AcceptLen, RestChars, Line} |
+%% {Action, AcceptLen, RestChars, Line, State} |
+%% {reject, AcceptLen, CurrTokLen, RestChars, Line, State} |
+%% {Action, AcceptLen, CurrTokLen, RestChars, Line, State}.
+%% Generated state transition functions. The non-accepting end state
+%% return signal either an unrecognised character or end of current
+%% input.
+
+-file("/Users/starbelly/devel/erlang/rebar3/src/hex_core/r3_safe_erl_term.erl", 360).
+yystate() -> 13.
+
+yystate(20, [60|Ics], Line, Tlen, Action, Alen) ->
+ yystate(10, Ics, Line, Tlen+1, Action, Alen);
+yystate(20, Ics, Line, Tlen, Action, Alen) ->
+ {Action,Alen,Tlen,Ics,Line,20};
+yystate(19, [92|Ics], Line, Tlen, Action, Alen) ->
+ yystate(11, Ics, Line, Tlen+1, Action, Alen);
+yystate(19, [39|Ics], Line, Tlen, Action, Alen) ->
+ yystate(15, Ics, Line, Tlen+1, Action, Alen);
+yystate(19, [10|Ics], Line, Tlen, Action, Alen) ->
+ yystate(0, Ics, Line+1, Tlen+1, Action, Alen);
+yystate(19, [C|Ics], Line, Tlen, Action, Alen) when C >= 0, C =< 9 ->
+ yystate(0, Ics, Line, Tlen+1, Action, Alen);
+yystate(19, [C|Ics], Line, Tlen, Action, Alen) when C >= 11, C =< 38 ->
+ yystate(0, Ics, Line, Tlen+1, Action, Alen);
+yystate(19, [C|Ics], Line, Tlen, Action, Alen) when C >= 40, C =< 91 ->
+ yystate(0, Ics, Line, Tlen+1, Action, Alen);
+yystate(19, [C|Ics], Line, Tlen, Action, Alen) when C >= 93 ->
+ yystate(0, Ics, Line, Tlen+1, Action, Alen);
+yystate(19, Ics, Line, Tlen, Action, Alen) ->
+ {Action,Alen,Tlen,Ics,Line,19};
+yystate(18, [92|Ics], Line, Tlen, Action, Alen) ->
+ yystate(14, Ics, Line, Tlen+1, Action, Alen);
+yystate(18, [34|Ics], Line, Tlen, Action, Alen) ->
+ yystate(6, Ics, Line, Tlen+1, Action, Alen);
+yystate(18, [10|Ics], Line, Tlen, Action, Alen) ->
+ yystate(9, Ics, Line+1, Tlen+1, Action, Alen);
+yystate(18, [C|Ics], Line, Tlen, Action, Alen) when C >= 0, C =< 9 ->
+ yystate(9, Ics, Line, Tlen+1, Action, Alen);
+yystate(18, [C|Ics], Line, Tlen, Action, Alen) when C >= 11, C =< 33 ->
+ yystate(9, Ics, Line, Tlen+1, Action, Alen);
+yystate(18, [C|Ics], Line, Tlen, Action, Alen) when C >= 35, C =< 91 ->
+ yystate(9, Ics, Line, Tlen+1, Action, Alen);
+yystate(18, [C|Ics], Line, Tlen, Action, Alen) when C >= 93 ->
+ yystate(9, Ics, Line, Tlen+1, Action, Alen);
+yystate(18, Ics, Line, Tlen, Action, Alen) ->
+ {Action,Alen,Tlen,Ics,Line,18};
+yystate(17, [37|Ics], Line, Tlen, _, _) ->
+ yystate(8, Ics, Line, Tlen+1, 8, Tlen);
+yystate(17, [10|Ics], Line, Tlen, _, _) ->
+ yystate(17, Ics, Line+1, Tlen+1, 8, Tlen);
+yystate(17, [C|Ics], Line, Tlen, _, _) when C >= 0, C =< 9 ->
+ yystate(17, Ics, Line, Tlen+1, 8, Tlen);
+yystate(17, [C|Ics], Line, Tlen, _, _) when C >= 11, C =< 32 ->
+ yystate(17, Ics, Line, Tlen+1, 8, Tlen);
+yystate(17, Ics, Line, Tlen, _, _) ->
+ {8,Tlen,Ics,Line,17};
+yystate(16, Ics, Line, Tlen, _, _) ->
+ {4,Tlen,Ics,Line};
+yystate(15, [92|Ics], Line, Tlen, _, _) ->
+ yystate(11, Ics, Line, Tlen+1, 1, Tlen);
+yystate(15, [39|Ics], Line, Tlen, _, _) ->
+ yystate(7, Ics, Line, Tlen+1, 1, Tlen);
+yystate(15, [10|Ics], Line, Tlen, _, _) ->
+ yystate(0, Ics, Line+1, Tlen+1, 1, Tlen);
+yystate(15, [C|Ics], Line, Tlen, _, _) when C >= 0, C =< 9 ->
+ yystate(0, Ics, Line, Tlen+1, 1, Tlen);
+yystate(15, [C|Ics], Line, Tlen, _, _) when C >= 11, C =< 38 ->
+ yystate(0, Ics, Line, Tlen+1, 1, Tlen);
+yystate(15, [C|Ics], Line, Tlen, _, _) when C >= 40, C =< 91 ->
+ yystate(0, Ics, Line, Tlen+1, 1, Tlen);
+yystate(15, [C|Ics], Line, Tlen, _, _) when C >= 93 ->
+ yystate(0, Ics, Line, Tlen+1, 1, Tlen);
+yystate(15, Ics, Line, Tlen, _, _) ->
+ {1,Tlen,Ics,Line,15};
+yystate(14, [94|Ics], Line, Tlen, Action, Alen) ->
+ yystate(18, Ics, Line, Tlen+1, Action, Alen);
+yystate(14, [93|Ics], Line, Tlen, Action, Alen) ->
+ yystate(9, Ics, Line, Tlen+1, Action, Alen);
+yystate(14, [92|Ics], Line, Tlen, Action, Alen) ->
+ yystate(14, Ics, Line, Tlen+1, Action, Alen);
+yystate(14, [34|Ics], Line, Tlen, Action, Alen) ->
+ yystate(6, Ics, Line, Tlen+1, Action, Alen);
+yystate(14, [10|Ics], Line, Tlen, Action, Alen) ->
+ yystate(9, Ics, Line+1, Tlen+1, Action, Alen);
+yystate(14, [C|Ics], Line, Tlen, Action, Alen) when C >= 0, C =< 9 ->
+ yystate(9, Ics, Line, Tlen+1, Action, Alen);
+yystate(14, [C|Ics], Line, Tlen, Action, Alen) when C >= 11, C =< 33 ->
+ yystate(9, Ics, Line, Tlen+1, Action, Alen);
+yystate(14, [C|Ics], Line, Tlen, Action, Alen) when C >= 35, C =< 91 ->
+ yystate(9, Ics, Line, Tlen+1, Action, Alen);
+yystate(14, [C|Ics], Line, Tlen, Action, Alen) when C >= 95 ->
+ yystate(9, Ics, Line, Tlen+1, Action, Alen);
+yystate(14, Ics, Line, Tlen, Action, Alen) ->
+ {Action,Alen,Tlen,Ics,Line,14};
+yystate(13, [125|Ics], Line, Tlen, Action, Alen) ->
+ yystate(16, Ics, Line, Tlen+1, Action, Alen);
+yystate(13, [123|Ics], Line, Tlen, Action, Alen) ->
+ yystate(16, Ics, Line, Tlen+1, Action, Alen);
+yystate(13, [93|Ics], Line, Tlen, Action, Alen) ->
+ yystate(16, Ics, Line, Tlen+1, Action, Alen);
+yystate(13, [91|Ics], Line, Tlen, Action, Alen) ->
+ yystate(16, Ics, Line, Tlen+1, Action, Alen);
+yystate(13, [62|Ics], Line, Tlen, Action, Alen) ->
+ yystate(2, Ics, Line, Tlen+1, Action, Alen);
+yystate(13, [61|Ics], Line, Tlen, Action, Alen) ->
+ yystate(2, Ics, Line, Tlen+1, Action, Alen);
+yystate(13, [60|Ics], Line, Tlen, Action, Alen) ->
+ yystate(20, Ics, Line, Tlen+1, Action, Alen);
+yystate(13, [47|Ics], Line, Tlen, Action, Alen) ->
+ yystate(4, Ics, Line, Tlen+1, Action, Alen);
+yystate(13, [46|Ics], Line, Tlen, Action, Alen) ->
+ yystate(3, Ics, Line, Tlen+1, Action, Alen);
+yystate(13, [39|Ics], Line, Tlen, Action, Alen) ->
+ yystate(0, Ics, Line, Tlen+1, Action, Alen);
+yystate(13, [37|Ics], Line, Tlen, Action, Alen) ->
+ yystate(8, Ics, Line, Tlen+1, Action, Alen);
+yystate(13, [35|Ics], Line, Tlen, Action, Alen) ->
+ yystate(16, Ics, Line, Tlen+1, Action, Alen);
+yystate(13, [34|Ics], Line, Tlen, Action, Alen) ->
+ yystate(9, Ics, Line, Tlen+1, Action, Alen);
+yystate(13, [10|Ics], Line, Tlen, Action, Alen) ->
+ yystate(17, Ics, Line+1, Tlen+1, Action, Alen);
+yystate(13, [C|Ics], Line, Tlen, Action, Alen) when C >= 0, C =< 9 ->
+ yystate(17, Ics, Line, Tlen+1, Action, Alen);
+yystate(13, [C|Ics], Line, Tlen, Action, Alen) when C >= 11, C =< 32 ->
+ yystate(17, Ics, Line, Tlen+1, Action, Alen);
+yystate(13, [C|Ics], Line, Tlen, Action, Alen) when C >= 43, C =< 45 ->
+ yystate(16, Ics, Line, Tlen+1, Action, Alen);
+yystate(13, [C|Ics], Line, Tlen, Action, Alen) when C >= 48, C =< 57 ->
+ yystate(12, Ics, Line, Tlen+1, Action, Alen);
+yystate(13, [C|Ics], Line, Tlen, Action, Alen) when C >= 97, C =< 122 ->
+ yystate(5, Ics, Line, Tlen+1, Action, Alen);
+yystate(13, Ics, Line, Tlen, Action, Alen) ->
+ {Action,Alen,Tlen,Ics,Line,13};
+yystate(12, [C|Ics], Line, Tlen, _, _) when C >= 48, C =< 57 ->
+ yystate(12, Ics, Line, Tlen+1, 3, Tlen);
+yystate(12, Ics, Line, Tlen, _, _) ->
+ {3,Tlen,Ics,Line,12};
+yystate(11, [94|Ics], Line, Tlen, Action, Alen) ->
+ yystate(19, Ics, Line, Tlen+1, Action, Alen);
+yystate(11, [93|Ics], Line, Tlen, Action, Alen) ->
+ yystate(0, Ics, Line, Tlen+1, Action, Alen);
+yystate(11, [92|Ics], Line, Tlen, Action, Alen) ->
+ yystate(11, Ics, Line, Tlen+1, Action, Alen);
+yystate(11, [39|Ics], Line, Tlen, Action, Alen) ->
+ yystate(15, Ics, Line, Tlen+1, Action, Alen);
+yystate(11, [10|Ics], Line, Tlen, Action, Alen) ->
+ yystate(0, Ics, Line+1, Tlen+1, Action, Alen);
+yystate(11, [C|Ics], Line, Tlen, Action, Alen) when C >= 0, C =< 9 ->
+ yystate(0, Ics, Line, Tlen+1, Action, Alen);
+yystate(11, [C|Ics], Line, Tlen, Action, Alen) when C >= 11, C =< 38 ->
+ yystate(0, Ics, Line, Tlen+1, Action, Alen);
+yystate(11, [C|Ics], Line, Tlen, Action, Alen) when C >= 40, C =< 91 ->
+ yystate(0, Ics, Line, Tlen+1, Action, Alen);
+yystate(11, [C|Ics], Line, Tlen, Action, Alen) when C >= 95 ->
+ yystate(0, Ics, Line, Tlen+1, Action, Alen);
+yystate(11, Ics, Line, Tlen, Action, Alen) ->
+ {Action,Alen,Tlen,Ics,Line,11};
+yystate(10, Ics, Line, Tlen, _, _) ->
+ {5,Tlen,Ics,Line};
+yystate(9, [92|Ics], Line, Tlen, Action, Alen) ->
+ yystate(14, Ics, Line, Tlen+1, Action, Alen);
+yystate(9, [34|Ics], Line, Tlen, Action, Alen) ->
+ yystate(1, Ics, Line, Tlen+1, Action, Alen);
+yystate(9, [10|Ics], Line, Tlen, Action, Alen) ->
+ yystate(9, Ics, Line+1, Tlen+1, Action, Alen);
+yystate(9, [C|Ics], Line, Tlen, Action, Alen) when C >= 0, C =< 9 ->
+ yystate(9, Ics, Line, Tlen+1, Action, Alen);
+yystate(9, [C|Ics], Line, Tlen, Action, Alen) when C >= 11, C =< 33 ->
+ yystate(9, Ics, Line, Tlen+1, Action, Alen);
+yystate(9, [C|Ics], Line, Tlen, Action, Alen) when C >= 35, C =< 91 ->
+ yystate(9, Ics, Line, Tlen+1, Action, Alen);
+yystate(9, [C|Ics], Line, Tlen, Action, Alen) when C >= 93 ->
+ yystate(9, Ics, Line, Tlen+1, Action, Alen);
+yystate(9, Ics, Line, Tlen, Action, Alen) ->
+ {Action,Alen,Tlen,Ics,Line,9};
+yystate(8, [37|Ics], Line, Tlen, _, _) ->
+ yystate(8, Ics, Line, Tlen+1, 8, Tlen);
+yystate(8, [10|Ics], Line, Tlen, _, _) ->
+ yystate(17, Ics, Line+1, Tlen+1, 8, Tlen);
+yystate(8, [C|Ics], Line, Tlen, _, _) when C >= 0, C =< 9 ->
+ yystate(8, Ics, Line, Tlen+1, 8, Tlen);
+yystate(8, [C|Ics], Line, Tlen, _, _) when C >= 11, C =< 32 ->
+ yystate(8, Ics, Line, Tlen+1, 8, Tlen);
+yystate(8, [C|Ics], Line, Tlen, _, _) when C >= 33, C =< 36 ->
+ yystate(8, Ics, Line, Tlen+1, 8, Tlen);
+yystate(8, [C|Ics], Line, Tlen, _, _) when C >= 38 ->
+ yystate(8, Ics, Line, Tlen+1, 8, Tlen);
+yystate(8, Ics, Line, Tlen, _, _) ->
+ {8,Tlen,Ics,Line,8};
+yystate(7, Ics, Line, Tlen, _, _) ->
+ {1,Tlen,Ics,Line};
+yystate(6, [92|Ics], Line, Tlen, _, _) ->
+ yystate(14, Ics, Line, Tlen+1, 2, Tlen);
+yystate(6, [34|Ics], Line, Tlen, _, _) ->
+ yystate(1, Ics, Line, Tlen+1, 2, Tlen);
+yystate(6, [10|Ics], Line, Tlen, _, _) ->
+ yystate(9, Ics, Line+1, Tlen+1, 2, Tlen);
+yystate(6, [C|Ics], Line, Tlen, _, _) when C >= 0, C =< 9 ->
+ yystate(9, Ics, Line, Tlen+1, 2, Tlen);
+yystate(6, [C|Ics], Line, Tlen, _, _) when C >= 11, C =< 33 ->
+ yystate(9, Ics, Line, Tlen+1, 2, Tlen);
+yystate(6, [C|Ics], Line, Tlen, _, _) when C >= 35, C =< 91 ->
+ yystate(9, Ics, Line, Tlen+1, 2, Tlen);
+yystate(6, [C|Ics], Line, Tlen, _, _) when C >= 93 ->
+ yystate(9, Ics, Line, Tlen+1, 2, Tlen);
+yystate(6, Ics, Line, Tlen, _, _) ->
+ {2,Tlen,Ics,Line,6};
+yystate(5, [95|Ics], Line, Tlen, _, _) ->
+ yystate(5, Ics, Line, Tlen+1, 0, Tlen);
+yystate(5, [64|Ics], Line, Tlen, _, _) ->
+ yystate(5, Ics, Line, Tlen+1, 0, Tlen);
+yystate(5, [C|Ics], Line, Tlen, _, _) when C >= 48, C =< 57 ->
+ yystate(5, Ics, Line, Tlen+1, 0, Tlen);
+yystate(5, [C|Ics], Line, Tlen, _, _) when C >= 65, C =< 90 ->
+ yystate(5, Ics, Line, Tlen+1, 0, Tlen);
+yystate(5, [C|Ics], Line, Tlen, _, _) when C >= 97, C =< 122 ->
+ yystate(5, Ics, Line, Tlen+1, 0, Tlen);
+yystate(5, Ics, Line, Tlen, _, _) ->
+ {0,Tlen,Ics,Line,5};
+yystate(4, Ics, Line, Tlen, _, _) ->
+ {7,Tlen,Ics,Line};
+yystate(3, Ics, Line, Tlen, _, _) ->
+ {6,Tlen,Ics,Line};
+yystate(2, [62|Ics], Line, Tlen, Action, Alen) ->
+ yystate(10, Ics, Line, Tlen+1, Action, Alen);
+yystate(2, Ics, Line, Tlen, Action, Alen) ->
+ {Action,Alen,Tlen,Ics,Line,2};
+yystate(1, Ics, Line, Tlen, _, _) ->
+ {2,Tlen,Ics,Line};
+yystate(0, [92|Ics], Line, Tlen, Action, Alen) ->
+ yystate(11, Ics, Line, Tlen+1, Action, Alen);
+yystate(0, [39|Ics], Line, Tlen, Action, Alen) ->
+ yystate(7, Ics, Line, Tlen+1, Action, Alen);
+yystate(0, [10|Ics], Line, Tlen, Action, Alen) ->
+ yystate(0, Ics, Line+1, Tlen+1, Action, Alen);
+yystate(0, [C|Ics], Line, Tlen, Action, Alen) when C >= 0, C =< 9 ->
+ yystate(0, Ics, Line, Tlen+1, Action, Alen);
+yystate(0, [C|Ics], Line, Tlen, Action, Alen) when C >= 11, C =< 38 ->
+ yystate(0, Ics, Line, Tlen+1, Action, Alen);
+yystate(0, [C|Ics], Line, Tlen, Action, Alen) when C >= 40, C =< 91 ->
+ yystate(0, Ics, Line, Tlen+1, Action, Alen);
+yystate(0, [C|Ics], Line, Tlen, Action, Alen) when C >= 93 ->
+ yystate(0, Ics, Line, Tlen+1, Action, Alen);
+yystate(0, Ics, Line, Tlen, Action, Alen) ->
+ {Action,Alen,Tlen,Ics,Line,0};
+yystate(S, Ics, Line, Tlen, Action, Alen) ->
+ {Action,Alen,Tlen,Ics,Line,S}.
+
+%% yyaction(Action, TokenLength, TokenChars, TokenLine) ->
+%% {token,Token} | {end_token, Token} | skip_token | {error,String}.
+%% Generated action function.
+
+yyaction(0, TokenLen, YYtcs, TokenLine) ->
+ TokenChars = yypre(YYtcs, TokenLen),
+ yyaction_0(TokenChars, TokenLine);
+yyaction(1, TokenLen, YYtcs, TokenLine) ->
+ TokenChars = yypre(YYtcs, TokenLen),
+ yyaction_1(TokenChars, TokenLen, TokenLine);
+yyaction(2, TokenLen, YYtcs, TokenLine) ->
+ TokenChars = yypre(YYtcs, TokenLen),
+ yyaction_2(TokenChars, TokenLen, TokenLine);
+yyaction(3, TokenLen, YYtcs, TokenLine) ->
+ TokenChars = yypre(YYtcs, TokenLen),
+ yyaction_3(TokenChars, TokenLine);
+yyaction(4, TokenLen, YYtcs, TokenLine) ->
+ TokenChars = yypre(YYtcs, TokenLen),
+ yyaction_4(TokenChars, TokenLine);
+yyaction(5, TokenLen, YYtcs, TokenLine) ->
+ TokenChars = yypre(YYtcs, TokenLen),
+ yyaction_5(TokenChars, TokenLine);
+yyaction(6, _, _, TokenLine) ->
+ yyaction_6(TokenLine);
+yyaction(7, _, _, TokenLine) ->
+ yyaction_7(TokenLine);
+yyaction(8, _, _, _) ->
+ yyaction_8();
+yyaction(_, _, _, _) -> error.
+
+-compile({inline,yyaction_0/2}).
+-file("/Users/starbelly/devel/erlang/rebar3/src/hex_core/r3_safe_erl_term.xrl", 14).
+yyaction_0(TokenChars, TokenLine) ->
+ tokenize_atom (TokenChars, TokenLine) .
+
+-compile({inline,yyaction_1/3}).
+-file("/Users/starbelly/devel/erlang/rebar3/src/hex_core/r3_safe_erl_term.xrl", 15).
+yyaction_1(TokenChars, TokenLen, TokenLine) ->
+ tokenize_atom (escape (unquote (TokenChars, TokenLen)), TokenLine) .
+
+-compile({inline,yyaction_2/3}).
+-file("/Users/starbelly/devel/erlang/rebar3/src/hex_core/r3_safe_erl_term.xrl", 16).
+yyaction_2(TokenChars, TokenLen, TokenLine) ->
+ { token, { string, TokenLine, escape (unquote (TokenChars, TokenLen)) } } .
+
+-compile({inline,yyaction_3/2}).
+-file("/Users/starbelly/devel/erlang/rebar3/src/hex_core/r3_safe_erl_term.xrl", 17).
+yyaction_3(TokenChars, TokenLine) ->
+ { token, { integer, TokenLine, list_to_integer (TokenChars) } } .
+
+-compile({inline,yyaction_4/2}).
+-file("/Users/starbelly/devel/erlang/rebar3/src/hex_core/r3_safe_erl_term.xrl", 18).
+yyaction_4(TokenChars, TokenLine) ->
+ { token, { list_to_atom (TokenChars), TokenLine } } .
+
+-compile({inline,yyaction_5/2}).
+-file("/Users/starbelly/devel/erlang/rebar3/src/hex_core/r3_safe_erl_term.xrl", 19).
+yyaction_5(TokenChars, TokenLine) ->
+ { token, { list_to_atom (TokenChars), TokenLine } } .
+
+-compile({inline,yyaction_6/1}).
+-file("/Users/starbelly/devel/erlang/rebar3/src/hex_core/r3_safe_erl_term.xrl", 20).
+yyaction_6(TokenLine) ->
+ { token, { dot, TokenLine } } .
+
+-compile({inline,yyaction_7/1}).
+-file("/Users/starbelly/devel/erlang/rebar3/src/hex_core/r3_safe_erl_term.xrl", 21).
+yyaction_7(TokenLine) ->
+ { token, { '/', TokenLine } } .
+
+-compile({inline,yyaction_8/0}).
+-file("/Users/starbelly/devel/erlang/rebar3/src/hex_core/r3_safe_erl_term.xrl", 22).
+yyaction_8() ->
+ skip_token .
+
+-file("/usr/local/lib/erlang/lib/parsetools-2.1.8/include/leexinc.hrl", 313).
diff --git a/src/r3_safe_erl_term.xrl b/src/r3_safe_erl_term.xrl
new file mode 100644
index 0000000..742aa6b
--- /dev/null
+++ b/src/r3_safe_erl_term.xrl
@@ -0,0 +1,79 @@
+%% Vendored from hex_core v0.5.0, do not edit manually
+
+%%% Author : Robert Virding
+%%% Purpose : Token definitions for Erlang.
+
+Definitions.
+
+D = [0-9]
+U = [A-Z]
+L = [a-z]
+A = ({U}|{L}|{D}|_|@)
+WS = ([\000-\s]|%.*)
+
+Rules.
+
+{L}{A}* : tokenize_atom(TokenChars, TokenLine).
+'(\\\^.|\\.|[^'])*' : tokenize_atom(escape(unquote(TokenChars, TokenLen)), TokenLine).
+"(\\\^.|\\.|[^"])*" : {token, {string, TokenLine, escape(unquote(TokenChars, TokenLen))}}.
+{D}+ : {token, {integer, TokenLine, list_to_integer(TokenChars)}}.
+[\#\[\]}{,+-] : {token, {list_to_atom(TokenChars), TokenLine}}.
+(<<|>>|=>) : {token, {list_to_atom(TokenChars), TokenLine}}.
+\. : {token, {dot, TokenLine}}.
+/ : {token, {'/', TokenLine}}.
+{WS}+ : skip_token.
+
+Erlang code.
+
+-export([terms/1]).
+
+terms(Tokens) ->
+ terms(Tokens, []).
+
+terms([{dot, _} = H], Buffer) ->
+ [buffer_to_term([H|Buffer])];
+terms([{dot, _} = H|T], Buffer) ->
+ [buffer_to_term([H|Buffer])|terms(T, [])];
+terms([H|T], Buffer) ->
+ terms(T, [H|Buffer]).
+
+buffer_to_term(Buffer) ->
+ {ok, Term} = erl_parse:parse_term(lists:reverse(Buffer)),
+ Term.
+
+unquote(TokenChars, TokenLen) ->
+ lists:sublist(TokenChars, 2, TokenLen - 2).
+
+tokenize_atom(TokenChars, TokenLine) ->
+ try list_to_existing_atom(TokenChars) of
+ Atom -> {token, {atom, TokenLine, Atom}}
+ catch
+ error:badarg -> {error, "illegal atom " ++ TokenChars}
+ end.
+
+escape([$\\|Cs]) ->
+ do_escape(Cs);
+escape([C|Cs]) ->
+ [C|escape(Cs)];
+escape([]) -> [].
+
+do_escape([O1,O2,O3|S]) when
+ O1 >= $0, O1 =< $7, O2 >= $0, O2 =< $7, O3 >= $0, O3 =< $7 ->
+ [(O1*8 + O2)*8 + O3 - 73*$0|escape(S)];
+do_escape([$^,C|Cs]) ->
+ [C band 31|escape(Cs)];
+do_escape([C|Cs]) when C >= $\000, C =< $\s ->
+ escape(Cs);
+do_escape([C|Cs]) ->
+ [escape_char(C)|escape(Cs)].
+
+escape_char($n) -> $\n; %\n = LF
+escape_char($r) -> $\r; %\r = CR
+escape_char($t) -> $\t; %\t = TAB
+escape_char($v) -> $\v; %\v = VT
+escape_char($b) -> $\b; %\b = BS
+escape_char($f) -> $\f; %\f = FF
+escape_char($e) -> $\e; %\e = ESC
+escape_char($s) -> $\s; %\s = SPC
+escape_char($d) -> $\d; %\d = DEL
+escape_char(C) -> C.
diff --git a/src/rebar.app.src b/src/rebar.app.src
index 6058efc..40d953b 100644
--- a/src/rebar.app.src
+++ b/src/rebar.app.src
@@ -30,7 +30,6 @@
relx,
cf,
inets,
- hex_core,
eunit_formatters]},
{env, [
%% Default log level
diff --git a/src/rebar_hex_repos.erl b/src/rebar_hex_repos.erl
index babaa32..ed5b1a8 100644
--- a/src/rebar_hex_repos.erl
+++ b/src/rebar_hex_repos.erl
@@ -20,6 +20,7 @@
api_url => binary(),
api_key => binary(),
repo_url => binary(),
+ repo_key => binary(),
repo_public_key => binary(),
repo_verify => binary(),
repo_verify_origin => binary()}.
@@ -68,17 +69,21 @@ repos(HexConfig) ->
merge_repos(RepoList ++ [HexDefaultConfig])
end.
-%% merge repos must add a field repo_name to work with hex_core 0.4.0
+%% merge repos must add a field repo_name to work with r3_hex_core 0.5.0
-spec merge_repos([repo()]) -> [repo()].
merge_repos(Repos) ->
- lists:foldl(fun(R=#{name := Name}, ReposAcc) ->
- %% private organizations include the parent repo before a :
+ lists:foldl(fun(R = #{name := Name}, ReposAcc) ->
+ %% private orgs are in the format of <<"parent:org">>
case rebar_string:split(Name, <<":">>) of
[Repo, Org] ->
- %% hex_core uses repo_name for parent
+
+ %% We set the repo_organization and api_organization to org
+ %% for fetching and publishing private packages.
update_repo_list(R#{name => Name,
- repo_name => Repo,
- organization => Org,
+ repo_name => Org,
+ repo_organization => Org,
+ api_organization => Org,
+ api_repository => Org,
parent => Repo}, ReposAcc);
_ ->
update_repo_list(R#{repo_name => Name}, ReposAcc)
@@ -86,15 +91,15 @@ merge_repos(Repos) ->
end, [], Repos).
update_organizations(Repos) ->
- lists:map(fun(Repo=#{organization := Organization,
+ lists:map(fun(Repo=#{repo_name := RepoName,
parent := ParentName}) ->
{ok, Parent} = get_repo_config(ParentName, Repos),
ParentRepoUrl = rebar_utils:to_list(maps:get(repo_url, Parent)),
- {ok, RepoUrl} =
+ {ok, _RepoUrl} =
rebar_utils:url_append_path(ParentRepoUrl,
- filename:join("repos", rebar_utils:to_list(Organization))),
+ filename:join("repos", rebar_utils:to_list(RepoName))),
%% still let the organization config override this constructed repo url
- maps:merge(Parent#{repo_url => rebar_utils:to_binary(RepoUrl)}, Repo);
+ maps:merge(Parent#{repo_url => rebar_utils:to_binary(ParentRepoUrl)}, Repo);
(Repo) ->
Repo
end, Repos).
@@ -107,7 +112,7 @@ update_repo_list(R, []) ->
[R].
default_repo() ->
- HexDefaultConfig = hex_core:default_config(),
+ HexDefaultConfig = r3_hex_core:default_config(),
HexDefaultConfig#{name => ?PUBLIC_HEX_REPO, repo_verify_origin => repo_verify_origin()}.
repo_verify_origin() ->
diff --git a/src/rebar_packages.erl b/src/rebar_packages.erl
index fc68cab..a260e47 100644
--- a/src/rebar_packages.erl
+++ b/src/rebar_packages.erl
@@ -31,7 +31,7 @@ format_error({missing_package, Pkg}) ->
-spec get(rebar_hex_repos:repo(), binary()) -> {ok, map()} | {error, term()}.
get(Config, Name) ->
- try hex_api_package:get(Config, Name) of
+ try r3_hex_api_package:get(Config, Name) of
{ok, {200, _Headers, PkgInfo}} ->
{ok, PkgInfo};
{ok, {404, _, _}} ->
@@ -233,7 +233,7 @@ parse_checksum(Checksum) ->
update_package(Name, RepoConfig=#{name := Repo}, State) ->
?MODULE:verify_table(State),
- try hex_repo:get_package(get_package_repo_config(RepoConfig), Name) of
+ try r3_hex_repo:get_package(get_package_repo_config(RepoConfig), Name) of
{ok, {200, _Headers, Releases}} ->
_ = insert_releases(Name, Releases, Repo, ?PACKAGE_TABLE),
{ok, RegistryDir} = rebar_packages:registry_dir(State),
@@ -244,7 +244,7 @@ update_package(Name, RepoConfig=#{name := Repo}, State) ->
fail;
Error ->
?DEBUG("Hex get_package request failed: ~p", [Error]),
- %% TODO: add better log message. hex_core should export a format_error
+ %% TODO: add better log message. r3_hex_core should export a format_error
?WARN("Failed to update package from repo ~ts", [Repo]),
fail
catch
@@ -255,8 +255,6 @@ update_package(Name, RepoConfig=#{name := Repo}, State) ->
get_package_repo_config(RepoConfig=#{mirror_of := Repo}) ->
get_package_repo_config(maps:remove(mirror_of, RepoConfig#{name => Repo}));
-get_package_repo_config(RepoConfig=#{read_key := Key}) ->
- get_package_repo_config(maps:remove(read_key, RepoConfig#{repo_key => Key}));
get_package_repo_config(RepoConfig) ->
RepoConfig.
diff --git a/src/rebar_pkg_resource.erl b/src/rebar_pkg_resource.erl
index 823b7fc..5de2437 100644
--- a/src/rebar_pkg_resource.erl
+++ b/src/rebar_pkg_resource.erl
@@ -29,7 +29,7 @@
-spec init(atom(), rebar_state:t()) -> {ok, rebar_resource_v2:resource()}.
init(Type, State) ->
{ok, Vsn} = application:get_key(rebar, vsn),
- BaseConfig = #{http_adapter => hex_http_httpc,
+ BaseConfig = #{http_adapter => r3_hex_http_httpc,
http_user_agent_fragment =>
<<"(rebar3/", (list_to_binary(Vsn))/binary, ") (httpc)">>,
http_adapter_config => #{profile => rebar}},
@@ -148,7 +148,7 @@ format_error({bad_registry_checksum, Name, Vsn, Expected, Found}) ->
-> {ok, cached} | {ok, binary(), binary()} | error.
request(Config, Name, Version, ETag) ->
Config1 = Config#{http_etag => ETag},
- try hex_repo:get_tarball(Config1, Name, Version) of
+ try r3_hex_repo:get_tarball(Config1, Name, Version) of
{ok, {200, #{<<"etag">> := ETag1}, Tarball}} ->
{ok, Tarball, ETag1};
{ok, {304, _Headers, _}} ->
@@ -248,7 +248,7 @@ serve_from_cache(TmpDir, CachePath, Pkg) ->
Res :: ok | {error,_} | {bad_registry_checksum, integer(), integer()}.
serve_from_memory(TmpDir, Binary, {pkg, _Name, _Vsn, Hash, _RepoConfig}) ->
RegistryChecksum = list_to_integer(binary_to_list(Hash), 16),
- case hex_tarball:unpack(Binary, TmpDir) of
+ case r3_hex_tarball:unpack(Binary, TmpDir) of
{ok, #{checksum := <<Checksum:256/big-unsigned>>}} when RegistryChecksum =/= Checksum ->
?DEBUG("Expected hash ~64.16.0B does not match checksum of fetched package ~64.16.0B",
[RegistryChecksum, Checksum]),
diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl
index 11add61..f1e440a 100644
--- a/src/rebar_utils.erl
+++ b/src/rebar_utils.erl
@@ -915,7 +915,7 @@ url_append_path(Url, ExtraPath) ->
case http_uri:parse(Url) of
{ok, {Scheme, UserInfo, Host, Port, Path, Query}} ->
{ok, lists:append([atom_to_list(Scheme), "://", UserInfo, Host, ":", integer_to_list(Port),
- filename:join(Path, ExtraPath), "?", Query])};
+ filename:join(Path, ExtraPath), Query])};
_ ->
error
end.