1#![allow(missing_docs)]
50
51use core::ffi::c_void;
52use core::ptr;
53use std::os::raw::{c_char, c_int};
54
55use crate::abi::allocator::{xmlFreeImpl, xmlMallocImpl, xmlMallocZero};
56use crate::abi::structs::{_xmlDoc, _xmlNode, _xmlNodeSet, _xmlXPathContext, _xmlXPathObject};
57use crate::abi::types::{xmlChar, xmlXPathObjectType};
58use crate::xml::xpath::types::{node_string_value, NodeSet, XPathValue};
59
60const VALUE_TAB_SIZE: usize = 10;
62
63#[repr(C)]
65#[derive(Debug)]
66pub struct XmlXPathParserContext {
67 pub cur: *const xmlChar,
68 pub base: *const xmlChar,
69 pub error: c_int,
70 pub context: *mut _xmlXPathContext,
71 pub value: *mut _xmlXPathObject,
72 pub value_nr: c_int,
73 pub value_max: c_int,
74 pub value_tab: *mut *mut _xmlXPathObject,
75 pub comp: *mut c_void,
76 pub xptr: c_int,
77 pub ancestor: *mut _xmlNode,
78 pub value_frame: c_int,
79}
80
81pub unsafe fn pc_set_error(pc: *mut XmlXPathParserContext, code: c_int) {
88 if pc.is_null() {
89 return;
90 }
91 unsafe { (*pc).error = code };
92}
93
94pub unsafe fn new_parser_context(
101 str_: *const xmlChar,
102 ctxt: *mut _xmlXPathContext,
103) -> *mut XmlXPathParserContext {
104 let pc = xmlMallocZero(size_of::<XmlXPathParserContext>()) as *mut XmlXPathParserContext;
105 if pc.is_null() {
106 return ptr::null_mut();
107 }
108 let tab = xmlMallocImpl(VALUE_TAB_SIZE * size_of::<*mut _xmlXPathObject>())
109 as *mut *mut _xmlXPathObject;
110 if tab.is_null() {
111 xmlFreeImpl(pc as *mut c_void);
112 return ptr::null_mut();
113 }
114 unsafe {
115 (*pc).cur = str_;
116 (*pc).base = str_;
117 (*pc).context = ctxt;
118 (*pc).value_nr = 0;
119 (*pc).value_max = VALUE_TAB_SIZE as c_int;
120 (*pc).value_tab = tab;
121 (*pc).value = ptr::null_mut();
122 (*pc).ancestor = ptr::null_mut();
123 (*pc).value_frame = 0;
124 }
125 pc
126}
127
128pub unsafe fn free_parser_context(pc: *mut XmlXPathParserContext) {
134 if pc.is_null() {
135 return;
136 }
137 unsafe {
138 let tab = (*pc).value_tab;
141 if !tab.is_null() {
142 for i in 0..(*pc).value_nr as usize {
143 let obj = *tab.add(i);
144 if !obj.is_null() {
145 crate::abi::exports_xml2::xmlXPathFreeObject(obj);
146 }
147 }
148 xmlFreeImpl(tab as *mut c_void);
149 }
150 xmlFreeImpl(pc as *mut c_void);
151 }
152}
153
154pub unsafe fn value_push(
160 pc: *mut XmlXPathParserContext,
161 val: *mut _xmlXPathObject,
162) -> *mut _xmlXPathObject {
163 if pc.is_null() {
164 return ptr::null_mut();
165 }
166 unsafe {
167 let nr = (*pc).value_nr as usize;
168 if nr >= VALUE_TAB_SIZE {
169 (*pc).error = crate::abi::types::XPATH_STACK_ERROR as c_int;
172 return val;
173 }
174 ptr::write((*pc).value_tab.add(nr), val);
175 (*pc).value_nr = nr as c_int + 1;
176 (*pc).value = val;
177 val
178 }
179}
180
181pub unsafe fn value_pop(pc: *mut XmlXPathParserContext) -> *mut _xmlXPathObject {
187 if pc.is_null() {
188 return ptr::null_mut();
189 }
190 unsafe {
191 let nr = (*pc).value_nr;
192 if nr <= 0 {
193 return ptr::null_mut();
194 }
195 let idx = (nr - 1) as usize;
196 let val = *(*pc).value_tab.add(idx);
197 ptr::write((*pc).value_tab.add(idx), ptr::null_mut());
198 (*pc).value_nr = nr - 1;
199 (*pc).value = if (*pc).value_nr > 0 {
200 *(*pc).value_tab.add((*pc).value_nr as usize - 1)
201 } else {
202 ptr::null_mut()
203 };
204 val
205 }
206}
207
208pub unsafe fn new_bool(b: bool) -> *mut _xmlXPathObject {
214 let obj = xmlMallocZero(size_of::<_xmlXPathObject>()) as *mut _xmlXPathObject;
215 if !obj.is_null() {
216 unsafe {
217 (*obj).type_ = xmlXPathObjectType::XPATH_BOOLEAN as c_int;
218 (*obj).boolval = if b { 1 } else { 0 };
219 }
220 }
221 obj
222}
223
224pub unsafe fn new_number(n: f64) -> *mut _xmlXPathObject {
236 let obj = xmlMallocZero(size_of::<_xmlXPathObject>()) as *mut _xmlXPathObject;
237 if !obj.is_null() {
238 unsafe {
239 (*obj).type_ = xmlXPathObjectType::XPATH_NUMBER as c_int;
240 (*obj).floatval = n;
241 }
242 }
243 obj
244}
245
246pub unsafe fn new_string(s: *const xmlChar) -> *mut _xmlXPathObject {
252 crate::xml::xpath::exports::xmlXPathNewString(s)
253}
254
255pub unsafe fn pop_number(pc: *mut XmlXPathParserContext) -> f64 {
262 if pc.is_null() {
263 return f64::NAN;
264 }
265 let arg = unsafe { value_pop(pc) };
266 if arg.is_null() {
267 return f64::NAN;
268 }
269 let n = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg).as_number();
270 crate::abi::exports_xml2::xmlXPathFreeObject(arg);
271 n
272}
273
274pub unsafe fn pop_boolean(pc: *mut XmlXPathParserContext) -> c_int {
280 if pc.is_null() {
281 return 0;
282 }
283 let arg = unsafe { value_pop(pc) };
284 if arg.is_null() {
285 return 0;
286 }
287 let b = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg).as_boolean();
288 crate::abi::exports_xml2::xmlXPathFreeObject(arg);
289 b as c_int
290}
291
292pub unsafe fn pop_string(pc: *mut XmlXPathParserContext) -> *mut xmlChar {
298 if pc.is_null() {
299 return ptr::null_mut();
300 }
301 let arg = unsafe { value_pop(pc) };
302 if arg.is_null() {
303 return ptr::null_mut();
304 }
305 let s = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg).as_string();
306 crate::abi::exports_xml2::xmlXPathFreeObject(arg);
307 crate::xml::string::xml_strndup(s.as_bytes().as_ptr() as *const xmlChar, s.len())
310}
311
312pub unsafe fn pop_node_set(pc: *mut XmlXPathParserContext) -> *mut _xmlNodeSet {
318 if pc.is_null() {
319 return ptr::null_mut();
320 }
321 let arg = unsafe { value_pop(pc) };
322 if arg.is_null() {
323 return ptr::null_mut();
324 }
325 let ns_ptr = unsafe { (*arg).nodesetval as *mut _xmlNodeSet };
326 unsafe { (*arg).nodesetval = ptr::null_mut() };
328 crate::abi::exports_xml2::xmlXPathFreeObject(arg);
329 ns_ptr
330}
331
332pub unsafe fn pop_external(pc: *mut XmlXPathParserContext) -> *mut c_void {
338 if pc.is_null() {
339 return ptr::null_mut();
340 }
341 let arg = unsafe { value_pop(pc) };
342 if arg.is_null() {
343 return ptr::null_mut();
344 }
345 let user = unsafe { (*arg).user };
346 crate::abi::exports_xml2::xmlXPathFreeObject(arg);
347 user
348}
349
350pub unsafe fn binary_number_op(pc: *mut XmlXPathParserContext, op: impl Fn(f64, f64) -> f64) {
359 let rhs = unsafe { pop_number(pc) };
360 let lhs = unsafe { pop_number(pc) };
361 let r = op(lhs, rhs);
362 unsafe { value_push(pc, new_number(r)) };
363}
364
365pub unsafe fn cast_top_to_number(pc: *mut XmlXPathParserContext) {
374 unsafe {
375 let val = (*pc).value;
376 if val.is_null() {
377 pc_set_error(pc, crate::abi::types::XPATH_INVALID_OPERAND as c_int);
378 return;
379 }
380 if (*val).type_ != xmlXPathObjectType::XPATH_NUMBER as c_int {
381 let v = crate::abi::exports_xml2::object_to_xpathvalue_pub(val);
382 (*val).floatval = v.as_number();
383 (*val).type_ = xmlXPathObjectType::XPATH_NUMBER as c_int;
384 }
385 }
386}
387
388fn node_set_pair_equal(a: &NodeSet, b: &NodeSet, neq: bool) -> bool {
391 if a.is_empty() || b.is_empty() {
392 return false;
393 }
394 if !neq {
397 for na in a.iter() {
398 if b.contains(na) {
399 return true;
400 }
401 }
402 }
403 for na in a.iter() {
404 let sa = node_string_value(na);
405 for nb in b.iter() {
406 let sb = node_string_value(nb);
407 if (sa == sb) ^ neq {
408 return true;
409 }
410 }
411 }
412 false
413}
414
415fn node_set_string_equal(a: &NodeSet, s: &str, neq: bool) -> bool {
417 if a.is_empty() {
418 return false;
419 }
420 for n in a.iter() {
421 let sv = node_string_value(n);
422 if sv == s {
423 if neq {
424 continue;
425 }
426 return true;
427 } else if neq {
428 return true;
429 }
430 }
431 false
432}
433
434fn node_set_number_equal(a: &NodeSet, f: f64, neq: bool) -> bool {
436 for n in a.iter() {
437 let sv = node_string_value(n);
438 let v = crate::xml::xpath::types::string_to_number(&sv);
439 if v.is_nan() {
440 if neq {
441 return true;
442 }
443 } else if (!neq && v == f) || (neq && v != f) {
444 return true;
445 }
446 }
447 false
448}
449
450pub fn equal_values_inner(v1: &XPathValue, v2: &XPathValue, neq: bool) -> bool {
454 match (v1, v2) {
457 (XPathValue::NodeSet(a), XPathValue::NodeSet(b)) => node_set_pair_equal(a, b, neq),
458 (XPathValue::NodeSet(a), other) => node_set_vs_value(a, other, neq),
459 (other, XPathValue::NodeSet(b)) => node_set_vs_value(b, other, neq),
460 _ => common_equal(v1, v2) ^ neq,
461 }
462}
463
464fn node_set_vs_value(ns: &NodeSet, other: &XPathValue, neq: bool) -> bool {
465 match other {
466 XPathValue::Boolean(b) => (!ns.is_empty()) == *b,
467 XPathValue::Number(f) => node_set_number_equal(ns, *f, neq),
468 XPathValue::String(s) => node_set_string_equal(ns, s, neq),
469 XPathValue::NodeSet(_) => unreachable!(),
471 }
472}
473
474fn common_equal(v1: &XPathValue, v2: &XPathValue) -> bool {
476 if matches!(v1, XPathValue::Boolean(_)) || matches!(v2, XPathValue::Boolean(_)) {
478 return v1.as_boolean() == v2.as_boolean();
479 }
480 if matches!(v1, XPathValue::Number(_)) || matches!(v2, XPathValue::Number(_)) {
483 return v1.as_number() == v2.as_number();
484 }
485 v1.as_string() == v2.as_string()
487}
488
489pub unsafe fn equal_values_impl(pc: *mut XmlXPathParserContext, neq: bool) -> c_int {
497 let arg2 = unsafe { value_pop(pc) };
498 if arg2.is_null() {
499 unsafe { (*pc).error = crate::abi::types::XPATH_INVALID_OPERAND as c_int };
500 return 0;
501 }
502 let arg1 = unsafe { value_pop(pc) };
503 if arg1.is_null() {
504 unsafe {
505 value_push(pc, arg2);
506 }
507 unsafe { (*pc).error = crate::abi::types::XPATH_INVALID_OPERAND as c_int };
508 return 0;
509 }
510 if arg1 == arg2 {
513 crate::abi::exports_xml2::xmlXPathFreeObject(arg1);
514 return if neq { 0 } else { 1 };
515 }
516 let v1 = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg1);
517 let v2 = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg2);
518 crate::abi::exports_xml2::xmlXPathFreeObject(arg1);
519 crate::abi::exports_xml2::xmlXPathFreeObject(arg2);
520
521 let eq = equal_values_inner(&v1, &v2, neq);
522 unsafe { value_push(pc, new_bool(eq)) };
523 eq as c_int
524}
525
526pub unsafe fn compare_values_impl(
533 pc: *mut XmlXPathParserContext,
534 inf: bool,
535 strict: bool,
536) -> c_int {
537 let arg2 = unsafe { value_pop(pc) };
538 if arg2.is_null() {
539 unsafe { (*pc).error = crate::abi::types::XPATH_INVALID_OPERAND as c_int };
540 return 0;
541 }
542 let arg1 = unsafe { value_pop(pc) };
543 if arg1.is_null() {
544 unsafe {
545 value_push(pc, arg2);
546 }
547 unsafe { (*pc).error = crate::abi::types::XPATH_INVALID_OPERAND as c_int };
548 return 0;
549 }
550
551 let v1 = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg1);
552 let v2 = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg2);
553 crate::abi::exports_xml2::xmlXPathFreeObject(arg1);
554 crate::abi::exports_xml2::xmlXPathFreeObject(arg2);
555
556 let (ns_side, other): (Option<&NodeSet>, Option<(&XPathValue, &XPathValue)>) = match (&v1, &v2)
557 {
558 (XPathValue::NodeSet(a), XPathValue::NodeSet(b)) => {
559 return compare_node_sets(a, b, inf, strict);
560 }
561 (XPathValue::NodeSet(a), _) => (Some(a), None),
562 (_, XPathValue::NodeSet(b)) => (Some(b), None),
563 _ => (None, Some((&v1, &v2))),
564 };
565
566 if let Some(ns) = ns_side {
567 let other = if matches!(&v1, XPathValue::NodeSet(_)) {
569 &v2
570 } else {
571 &v1
572 };
573 let (ns_is_left, scalar) = if matches!(&v1, XPathValue::NodeSet(_)) {
576 (true, other)
577 } else {
578 (false, other)
579 };
580 return compare_node_set_value(ns, scalar, ns_is_left, inf, strict);
581 }
582
583 let (l, r) = other.unwrap();
584 let a = l.as_number();
587 let b = r.as_number();
588 if a.is_nan() || b.is_nan() {
589 return 0;
590 }
591 let ret = if inf && strict {
592 a < b
593 } else if inf && !strict {
594 a <= b
595 } else if !inf && strict {
596 a > b
597 } else {
598 a >= b
599 };
600 ret as c_int
601}
602
603fn compare_node_sets(a: &NodeSet, b: &NodeSet, inf: bool, strict: bool) -> c_int {
611 if a.is_empty() || b.is_empty() {
612 return 0;
613 }
614 let b_nums: Vec<f64> = b.iter().map(|n| unsafe { node_to_number(n) }).collect();
615 for na in a.iter() {
616 let va = unsafe { node_to_number(na) };
617 if va.is_nan() {
618 continue;
619 }
620 for &vb in &b_nums {
621 if vb.is_nan() {
622 continue;
623 }
624 let ret = if inf && strict {
625 va < vb
626 } else if inf && !strict {
627 va <= vb
628 } else if !inf && strict {
629 va > vb
630 } else {
631 va >= vb
632 };
633 if ret {
634 return 1;
635 }
636 }
637 }
638 0
639}
640
641fn compare_node_set_value(
642 ns: &NodeSet,
643 scalar: &XPathValue,
644 ns_is_left: bool,
645 inf: bool,
646 strict: bool,
647) -> c_int {
648 match scalar {
649 XPathValue::Number(f) => compare_node_set_number(ns, *f, ns_is_left, inf, strict),
650 XPathValue::String(s) => compare_node_set_string(ns, s, ns_is_left, inf, strict),
651 XPathValue::Boolean(_) => {
652 let ns_bool = !ns.is_empty();
654 let b = scalar.as_boolean();
655 let (a, b) = if ns_is_left {
656 (ns_bool, b)
657 } else {
658 (b, ns_bool)
659 };
660 let ret = if inf && strict {
661 !a & b
662 } else if inf && !strict {
663 a <= b
664 } else if !inf && strict {
665 a & !b
666 } else {
667 a >= b
668 };
669 ret as c_int
670 }
671 _ => 0,
672 }
673}
674
675fn compare_node_set_number(
683 ns: &NodeSet,
684 f: f64,
685 ns_is_left: bool,
686 inf: bool,
687 strict: bool,
688) -> c_int {
689 for n in ns.iter() {
690 let v = unsafe { node_to_number(n) };
691 if v.is_nan() {
692 continue;
693 }
694 let (a, b) = if ns_is_left { (v, f) } else { (f, v) };
695 let ret = if inf && strict {
696 a < b
697 } else if inf && !strict {
698 a <= b
699 } else if !inf && strict {
700 a > b
701 } else {
702 a >= b
703 };
704 if ret {
705 return 1;
706 }
707 }
708 0
709}
710
711fn compare_node_set_string(
712 ns: &NodeSet,
713 s: &str,
714 ns_is_left: bool,
715 inf: bool,
716 strict: bool,
717) -> c_int {
718 for n in ns.iter() {
719 let sv = node_string_value(n);
720 let v = crate::xml::xpath::types::string_to_number(&sv);
721 let w = crate::xml::xpath::types::string_to_number(s);
722 if v.is_nan() || w.is_nan() {
723 continue;
724 }
725 let (a, b) = if ns_is_left { (v, w) } else { (w, v) };
726 let ret = if inf && strict {
727 a < b
728 } else if inf && !strict {
729 a <= b
730 } else if !inf && strict {
731 a > b
732 } else {
733 a >= b
734 };
735 if ret {
736 return 1;
737 }
738 }
739 0
740}
741
742unsafe fn node_to_number(node: *mut _xmlNode) -> f64 {
745 if node.is_null() {
746 return f64::NAN;
747 }
748 let sv = node_string_value(node);
749 crate::xml::xpath::types::string_to_number(&sv)
750}
751
752pub unsafe fn value_to_object(v: XPathValue) -> *mut _xmlXPathObject {
758 crate::abi::exports_xml2::xpath_to_object_pub(v)
759}
760
761#[allow(unused)]
762const fn _unused(_: *mut _xmlDoc) {}
763
764#[allow(unused)]
765const fn _unused_char(_: *const c_char) {}