%% Copyright (c) 2017-2026 Guilherme Andrade %% %% Permission is hereby granted, free of charge, to any person obtaining a %% copy of this software and associated documentation files (the "Software"), %% to deal in the Software without restriction, including without limitation %% the rights to use, copy, modify, merge, publish, distribute, sublicense, %% and/or sell copies of the Software, and to permit persons to whom the %% Software is furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER %% DEALINGS IN THE SOFTWARE. %% %% Yabko is an independent project and has not been authorized, sponsored, %% or otherwise approved by Apple Inc. -module(yabko). -ifdef(E48). -moduledoc """ A parser of Apple Property Lists (.plist). Both the XML and the binary (`bplist`) encodings are decoded through the single entry point `decode/1`. """. -endif. -include("yabko_common.hrl"). -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). -endif. %% ------------------------------------------------------------------ %% API Function Exports %% ------------------------------------------------------------------ -export([decode/1]). -ignore_xref({decode, 1}). %% ------------------------------------------------------------------ %% Type Definitions %% ------------------------------------------------------------------ -ifdef(E48). -doc "A decoded property list value.". -endif. -type object() :: undefined | boolean() | int64() | float() | calendar:datetime() | {uid, uint64()} | [object()] | #{binary() => object()}. -export_type([object/0]). -type int64() :: -9223372036854775808..9223372036854775807. -export_type([int64/0]). -type uint64() :: 0..18446744073709551615. -export_type([uint64/0]). %% ------------------------------------------------------------------ %% API Function Definitions %% ------------------------------------------------------------------ -ifdef(E48). -doc """ Decode an encoded property list. `Data` may hold either the XML or the binary (`bplist`) representation; the format is detected automatically. Returns `{ok, DecodedObject}` on success or `{error, {exception, Class, Reason, Stacktrace}}` if decoding raised. """. -endif. -spec decode(Data) -> {ok, DecodedObject} | {error, Error} when Data :: iodata(), DecodedObject :: object(), Error :: {exception, atom(), term(), [term()]}. decode(<<"bplist", Version:2/binary, EncodedPList/binary>>) when Version =:= <<"00">>; Version =:= <<"01">> -> try yabko_bin:decode(EncodedPList, 8) of PList -> {ok, PList} catch Class:Reason:Stacktrace -> {error, {exception, Class, Reason, Stacktrace}} end; decode(<>) -> try yabko_xml:decode(EncodedPList) of PList -> {ok, PList} catch Class:Reason:Stacktrace -> {error, {exception, Class, Reason, Stacktrace}} end; decode(IoData) -> Binary = iolist_to_binary(IoData), decode(Binary). %% ------------------------------------------------------------------ %% Unit Tests %% ------------------------------------------------------------------ -ifdef(TEST). bin_decode_test() -> run_test_helper("test_data/test.bin.plist", {ok, expected_generic_test_data()}). xml_decode_test() -> run_test_helper("test_data/test.xml.plist", {ok, expected_generic_test_data()}). uid_decode_bin_test() -> % taken from https://github.com/rodneyrehm/CFPropertyList run_test_helper("test_data/uid-list.plist", {ok, expected_uid_test_data()}). uid_decode_xml_test() -> % taken from https://github.com/rodneyrehm/CFPropertyList run_test_helper("test_data/uid-list.xml", {ok, expected_uid_test_data()}). bin_unicode_decode_test() -> run_test_helper("test_data/unicode.bin.plist", {ok, expected_unicode_test_data()}). xml_unicode_decode_test() -> run_test_helper("test_data/unicode.xml.plist", {ok, expected_unicode_test_data()}). float32_decode_test() -> run_test_helper("test_data/float32.bin.plist", {ok, #{<<"etc etc..">> => 1.0}}). run_test_helper(Path, ExpectedResult) -> {ok, Encoded} = file:read_file(Path), ?assertEqual(ExpectedResult, decode(Encoded)). expected_generic_test_data() -> #{ <<"Lincoln">> => #{ <<"DOB">> => {{1809, 2, 12}, {9, 18, 0}}, <<"Eulogy">> => <<183, 5, 15, 253, 33, 21, 131, 249, 92, 108, 122, 222, 146, 36, 48, 160, 55, 207, 61, 19, 93, 90, 230, 12, 62, 148, 58, 9, 80, 96, 143, 66, 31, 185, 212, 80, 46, 38, 211, 39, 178, 206, 171, 74, 45, 85, 147, 156, 163, 192, 214, 241, 175, 82, 155, 80, 17, 145, 231, 188, 98, 37, 206, 74, 238, 249, 180, 115, 103, 174, 202, 250, 152, 186, 185, 42, 89, 84, 189, 191, 69, 196, 134, 21, 63, 96, 106, 0, 52, 224, 177, 7, 41, 108, 77, 50, 128, 206, 137, 167, 52, 237, 20, 147, 139, 204, 227, 182, 93, 79, 2, 206, 138, 30, 60, 3, 248, 35, 31, 164, 98, 207, 116, 224, 244, 24, 204, 63, 173, 160, 113, 53, 114, 230, 166, 104, 141, 6, 0, 151>>, <<"IsNamedGeorge">> => false, <<"Name">> => <<"Abraham Lincoln">>, <<"Scores">> => [ 8, -8, 512, -512, 65536, -65536, 4294967296, -4294967296, 3.14, -3.14, 4.900000095367432, -4.900000095367432 ] }, <<"Washington">> => #{ <<"DOB">> => {{1732, 2, 17}, {1, 32, 0}}, <<"Eulogy">> => <<19, 220, 90, 23, 220, 229, 7, 1, 57, 36, 3, 160, 249, 63, 76, 151, 169, 162, 164, 181>>, <<"IsNamedGeorge">> => true, <<"Name">> => <<"George Washington">>, <<"Scores">> => [6, 4.599999904632568, 6] } }. expected_uid_test_data() -> #{ <<"small">> => {uid, 1}, <<"medium">> => {uid, 256}, <<"large">> => {uid, 65536}, <<"huge">> => {uid, 4294967296} }. expected_unicode_test_data() -> #{ <<"A thing">> => << "他度於樂。實術醫收參不民對續子義發性親的論草像終因:" "地己關物理應在她決精……在論被主馬路兩關保分怕件比人" "……孩經感發特,那經情你相,破族木相資個外生" "。熱民待……天女度之竟賽外的史次道就就只……動技會," "世國系笑早,四司作方極中面:晚寫許或美大下正人濟" "保樂這候可著了下供半於受下唱素動古下。"/utf8 >> }. -endif.