Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/xmerl/src/xmerl_ucs.erl
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ is_incharset(In,Charset) when is_list(In) ->
{error,unsupported_charset};
{error,_} ->
false;
[Int] when is_integer(Int) ->
L when is_list(L) ->
true
end.

Expand Down
32 changes: 18 additions & 14 deletions lib/xmerl/src/xmerl_xpath_pred.erl
Original file line number Diff line number Diff line change
Expand Up @@ -166,15 +166,15 @@ comp_expr('>', E1, E2, C) ->
comp_expr('<', E1, E2, C) ->
N1 = expr(E1,C),
N2 = expr(E2,C),
?boolean(compare_ineq_format(N1,N2,C) > compare_ineq_format(N2,N1,C));
?boolean(compare_ineq_format(N1,N2,C) < compare_ineq_format(N2,N1,C));
comp_expr('>=', E1, E2, C) ->
N1 = expr(E1,C),
N2 = expr(E2,C),
?boolean(compare_ineq_format(N1,N2,C) > compare_ineq_format(N2,N1,C));
?boolean(compare_ineq_format(N1,N2,C) >= compare_ineq_format(N2,N1,C));
comp_expr('<=', E1, E2, C) ->
N1 = expr(E1,C),
N2 = expr(E2,C),
?boolean(compare_ineq_format(N1,N2,C) > compare_ineq_format(N2,N1,C));
?boolean(compare_ineq_format(N1,N2,C) =< compare_ineq_format(N2,N1,C));
comp_expr('=', E1, E2, C) ->
N1 = expr(E1,C),
N2 = expr(E2,C),
Expand Down Expand Up @@ -508,8 +508,12 @@ contains(C, [A1, A2]) ->
'substring-before'(C, [A1, A2]) ->
S1 = mk_string(C, A1),
S2 = mk_string(C, A2),
Pos = string:str(S1, S2),
?string(string:substr(S1, 1, Pos)).
case string:str(S1, S2) of
0 ->
?string([]);
Pos ->
?string(string:substr(S1, 1, Pos - 1))
end.

%% string: substring-after(string, string)
'substring-after'(C, [A1, A2]) ->
Expand All @@ -536,10 +540,10 @@ substring(C, [A1, A2, A3]) ->

%% number: string-length(string?)
'string-length'(C = #xmlContext{context_node = N}, []) ->
length(mk_string(C, string_value(N)));
?number(length(mk_string(C, string_value(N))));

'string-length'(C, [A]) ->
length(mk_string(C, A)).
?number(length(mk_string(C, A))).


%% string: normalize-space(string?)
Expand Down Expand Up @@ -633,17 +637,17 @@ match_lang(_, _) ->

%% number: number(object)
number(C = #xmlContext{context_node = N}, []) ->
?number(mk_number(C, string(C, N)));
?number(mk_number(C, string_value(N)));
number(C, [Arg]) ->
?number(mk_number(C, Arg)).


sum(C, [Arg]) ->
NS = mk_nodeset(C, Arg),
lists:foldl(
fun(N, Sum) ->
Sum + mk_number(C, string(C, N))
end, 0, NS).
?number(lists:foldl(
fun(N, Sum) ->
Sum + mk_number(C, string_value(N))
end, 0, NS)).

floor(C, [Arg]) ->
Num = mk_number(C, Arg),
Expand All @@ -665,14 +669,14 @@ ceiling(C, [Arg]) ->


round(C, [Arg]) ->
case mk_number(C, Arg) of
?number(case mk_number(C, Arg) of
A when is_atom(A) ->
A;
N when is_integer(N) ->
N;
F when is_float(F) ->
round(F)
end.
end).


select_on_attribute([E = #xmlElement{attributes = Attrs}|T], K, V, Acc) ->
Expand Down
17 changes: 16 additions & 1 deletion lib/xmerl/test/xmerl_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
all() ->
[doctests,
{group, cpd_tests}, xpath_text1, xpath_main,
xpath_abbreviated_syntax, xpath_functions, xpath_namespaces,
xpath_abbreviated_syntax, xpath_functions, xpath_relational,
xpath_namespaces, ucs_is_incharset,
{group, misc}, {group, eventp_tests},
{group, ticket_tests}, {group, app_test},
{group, appup_test}, {group, format_test}].
Expand Down Expand Up @@ -192,6 +193,10 @@ xpath_functions(Config) ->
file:set_cwd(filename:join(datadir(Config),xpath)),
ok = xpath_abbrev:functions().

xpath_relational(Config) ->
file:set_cwd(filename:join(datadir(Config),xpath)),
ok = xpath_abbrev:relational_operators().

xpath_namespaces(Config) ->
file:set_cwd(filename:join(datadir(Config),xpath)),
ok = xpath_abbrev:namespaces().
Expand Down Expand Up @@ -673,6 +678,16 @@ allow_entities_test(Config) ->
(catch xmerl_scan:file(File, [{allow_entities, false}])),
ok.

%% Regression: xmerl_ucs:is_incharset/2 crashed with a case_clause on a
%% multi-character list for charsets handled via to_unicode/2 (e.g. utf-8),
%% because the clause only matched a single-element result.
ucs_is_incharset(_Config) ->
true = xmerl_ucs:is_incharset("abc", 'utf-8'),
true = xmerl_ucs:is_incharset("a", 'utf-8'),
true = xmerl_ucs:is_incharset("abc", 'iso-8859-1'),
false = xmerl_ucs:is_incharset([256, 257], 'iso-8859-1'),
ok.

%%======================================================================
%% Support Functions
%%======================================================================
Expand Down
72 changes: 61 additions & 11 deletions lib/xmerl/test/xmerl_SUITE_data/xpath/xpath_abbrev.erl
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
%%%-------------------------------------------------------------------
%%% File : xpath_abbrev.erl
%%% Author : Bertil Karlsson <bertil@finrod>
%%% Description :
%%%
%%% Created : 17 Jan 2006 by Bertil Karlsson <bertil@finrod>
%%%-------------------------------------------------------------------
%% %CopyrightBegin%
%%
%% SPDX-License-Identifier: Apache-2.0
%%
%% Copyright Ericsson AB 2026. 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(xpath_abbrev).

-export([test/0, check_node_set/2, ticket_6873/0, ticket_7496/0, functions/0]).
-export([relational_operators/0]).
-export([namespaces/0]).

-include_lib("common_test/include/ct.hrl").
Expand Down Expand Up @@ -61,7 +75,7 @@ test() ->
Res22 = xmerl_xpath:string("blipp[@id and @test]",E),
ok = check_node_set("blipp[@id and @test]",Res22).

check_node_set("blipp",[E1,E2,E3]) ->
check_node_set("blipp",[E1,E2,E3]) ->
ok = xml_element_name(E1,blipp),
ok = xml_element_name(E2,blipp),
ok = xml_element_name(E3,blipp),
Expand Down Expand Up @@ -106,7 +120,7 @@ check_node_set("/myBS_model/blipp[3]/blupp[2]",[E]) ->
#xmlElement{name=blupp,
attributes=[#xmlAttribute{name=att,value="bluppc2"}]}=E,
ok;
check_node_set("blipp//plopp",[#xmlElement{name=plopp},#xmlElement{name=plopp}]) ->
check_node_set("blipp//plopp",[#xmlElement{name=plopp},#xmlElement{name=plopp}]) ->
ok;
check_node_set("//plopp",[E1,E2,E3]) ->
ok = xml_element_name(E1,plopp),
Expand Down Expand Up @@ -218,6 +232,33 @@ ticket_7496() ->
ok = Test(Doc3,"//*[starts-with(local-name(),'p')]",
[parent,pet,pet]).

%% Regression test for the relational operators '<', '<=' and '>='.
%% These three clauses of xmerl_xpath_pred:comp_expr/4 each carried the
%% body of '>' (a copy-paste error), so every one evaluated as 'a > b'.
%% The cases below fail on the buggy code (each selects [e,f] / [] instead
%% of the expected node sets) and pass once the operators are correct.
relational_operators() ->
Test = fun(Doc, XPath, Exp) ->
Result = xmerl_xpath:string(XPath, Doc),
Exp = [Name || #xmlElement{name = Name} <- Result],
ok
end,
{Doc,_} = xmerl_scan:string("<a><b/><c/><d/><e/><f/></a>"),

%% Relational predicates over position() (number vs number).
ok = Test(Doc, "/a/*[position() < 3]", [b, c]),
ok = Test(Doc, "/a/*[position() <= 3]", [b, c, d]),
ok = Test(Doc, "/a/*[position() >= 3]", [d, e, f]),
ok = Test(Doc, "/a/*[position() > 3]", [e, f]),

%% Constant relational predicates, independent of position(): each is a
%% boolean that is true, so it selects every child.
All = [b, c, d, e, f],
ok = Test(Doc, "/a/*[2 < 3]", All),
ok = Test(Doc, "/a/*[3 <= 3]", All),
ok = Test(Doc, "/a/*[3 >= 3]", All),
ok = Test(Doc, "/a/*[3 > 2]", All),
ok.

functions() ->
Test = fun(Doc, XPath, Exp) ->
Expand All @@ -234,7 +275,7 @@ functions() ->
end|| Obj <- Result],
ok
end,
Foo =
Foo =
"<foo>"
" <bar>"
" <name>Xml</name>"
Expand All @@ -257,6 +298,16 @@ functions() ->
ok = Test(Doc,"/foo/bar[starts-with(name, 'X')]",[bar,bar]),
ok = Test(Doc,"/foo/bar[value = string(1)]/value/text()",["1"]),

%% Regression tests: substring-before used to include the separator's
%% first character (off-by-one); string-length and round returned bare
%% numbers instead of #xmlObj{type=number}; and sum and number() (no
%% args) called string/2 with a bare node, crashing on any non-empty
%% node-set.
ok = Test(Doc,"/foo/bar[substring-before(name, 'a') = 'Xp']",[bar]),
ok = Test(Doc,"/foo/bar[string-length(name) = 3]",[bar]),
ok = Test(Doc,"/foo/bar[round(value) = 2]",[bar]),
ok = Test(Doc,"/foo[sum(bar/value) = 6]",[foo]),
ok = Test(Doc,"/foo/bar/value[number() = 2]",[value]),

{Doc2,_}= xmerl_scan:file("purchaseOrder.xml"),
ok = Test(Doc2,"//*[starts-with(local-name(),'c')]",
Expand All @@ -266,7 +317,6 @@ functions() ->
ok = Test(Doc2,"//*[starts-with(name(),'{http://www.example.com/PO1')]",
['apo:purchaseOrder','apo:comment']).


namespaces() ->
{Doc,_} = xmerl_scan:file("purchaseOrder.xml", [{namespace_conformant, true}]),

Expand Down
Loading