1#![allow(missing_docs)]
33
34use core::ffi::c_void;
35use core::ptr;
36use std::os::raw::{c_char, c_int};
37
38use crate::abi::allocator::{xmlFreeImpl, xmlMallocImpl, xmlMallocZero};
39use crate::abi::structs::{_xmlDoc, _xmlNode, _xmlNodeSet, _xmlXPathContext, _xmlXPathObject};
40use crate::abi::types::{xmlChar, xmlXPathObjectType};
41use crate::xml::xpath::types::{node_string_value, NodeSet, XPathValue};
42
43const VALUE_TAB_SIZE: usize = 10;
45
46#[repr(C)]
48#[derive(Debug)]
49pub struct XmlXPathParserContext {
50 pub cur: *const xmlChar,
51 pub base: *const xmlChar,
52 pub error: c_int,
53 pub context: *mut _xmlXPathContext,
54 pub value: *mut _xmlXPathObject,
55 pub value_nr: c_int,
56 pub value_max: c_int,
57 pub value_tab: *mut *mut _xmlXPathObject,
58 pub comp: *mut c_void,
59 pub xptr: c_int,
60 pub ancestor: *mut _xmlNode,
61 pub value_frame: c_int,
62}
63
64pub unsafe fn pc_set_error(pc: *mut XmlXPathParserContext, code: c_int) {
71 if pc.is_null() {
72 return;
73 }
74 unsafe { (*pc).error = code };
75}
76
77pub unsafe fn new_parser_context(
84 str_: *const xmlChar,
85 ctxt: *mut _xmlXPathContext,
86) -> *mut XmlXPathParserContext {
87 let pc = xmlMallocZero(size_of::<XmlXPathParserContext>()) as *mut XmlXPathParserContext;
88 if pc.is_null() {
89 return ptr::null_mut();
90 }
91 let tab = xmlMallocImpl(VALUE_TAB_SIZE * size_of::<*mut _xmlXPathObject>())
92 as *mut *mut _xmlXPathObject;
93 if tab.is_null() {
94 xmlFreeImpl(pc as *mut c_void);
95 return ptr::null_mut();
96 }
97 unsafe {
98 (*pc).cur = str_;
99 (*pc).base = str_;
100 (*pc).context = ctxt;
101 (*pc).value_nr = 0;
102 (*pc).value_max = VALUE_TAB_SIZE as c_int;
103 (*pc).value_tab = tab;
104 (*pc).value = ptr::null_mut();
105 (*pc).ancestor = ptr::null_mut();
106 (*pc).value_frame = 0;
107 }
108 pc
109}
110
111pub unsafe fn free_parser_context(pc: *mut XmlXPathParserContext) {
117 if pc.is_null() {
118 return;
119 }
120 unsafe {
121 let tab = (*pc).value_tab;
124 if !tab.is_null() {
125 for i in 0..(*pc).value_nr as usize {
126 let obj = *tab.add(i);
127 if !obj.is_null() {
128 crate::abi::exports_xml2::xmlXPathFreeObject(obj);
129 }
130 }
131 xmlFreeImpl(tab as *mut c_void);
132 }
133 xmlFreeImpl(pc as *mut c_void);
134 }
135}
136
137pub unsafe fn value_push(
143 pc: *mut XmlXPathParserContext,
144 val: *mut _xmlXPathObject,
145) -> *mut _xmlXPathObject {
146 if pc.is_null() {
147 return ptr::null_mut();
148 }
149 unsafe {
150 let nr = (*pc).value_nr as usize;
151 if nr >= VALUE_TAB_SIZE {
152 (*pc).error = crate::abi::types::XPATH_STACK_ERROR as c_int;
155 return val;
156 }
157 ptr::write((*pc).value_tab.add(nr), val);
158 (*pc).value_nr = nr as c_int + 1;
159 (*pc).value = val;
160 val
161 }
162}
163
164pub unsafe fn value_pop(pc: *mut XmlXPathParserContext) -> *mut _xmlXPathObject {
170 if pc.is_null() {
171 return ptr::null_mut();
172 }
173 unsafe {
174 let nr = (*pc).value_nr;
175 if nr <= 0 {
176 return ptr::null_mut();
177 }
178 let idx = (nr - 1) as usize;
179 let val = *(*pc).value_tab.add(idx);
180 ptr::write((*pc).value_tab.add(idx), ptr::null_mut());
181 (*pc).value_nr = nr - 1;
182 (*pc).value = if (*pc).value_nr > 0 {
183 *(*pc).value_tab.add((*pc).value_nr as usize - 1)
184 } else {
185 ptr::null_mut()
186 };
187 val
188 }
189}
190
191pub unsafe fn new_bool(b: bool) -> *mut _xmlXPathObject {
197 let obj = xmlMallocZero(size_of::<_xmlXPathObject>()) as *mut _xmlXPathObject;
198 if !obj.is_null() {
199 unsafe {
200 (*obj).type_ = xmlXPathObjectType::XPATH_BOOLEAN as c_int;
201 (*obj).boolval = if b { 1 } else { 0 };
202 }
203 }
204 obj
205}
206
207pub unsafe fn new_number(n: f64) -> *mut _xmlXPathObject {
219 let obj = xmlMallocZero(size_of::<_xmlXPathObject>()) as *mut _xmlXPathObject;
220 if !obj.is_null() {
221 unsafe {
222 (*obj).type_ = xmlXPathObjectType::XPATH_NUMBER as c_int;
223 (*obj).floatval = n;
224 }
225 }
226 obj
227}
228
229pub unsafe fn new_string(s: *const xmlChar) -> *mut _xmlXPathObject {
235 crate::xml::xpath::exports::xmlXPathNewString(s)
236}
237
238pub unsafe fn pop_number(pc: *mut XmlXPathParserContext) -> f64 {
245 if pc.is_null() {
246 return f64::NAN;
247 }
248 let arg = unsafe { value_pop(pc) };
249 if arg.is_null() {
250 return f64::NAN;
251 }
252 let n = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg).as_number();
253 crate::abi::exports_xml2::xmlXPathFreeObject(arg);
254 n
255}
256
257pub unsafe fn pop_boolean(pc: *mut XmlXPathParserContext) -> c_int {
263 if pc.is_null() {
264 return 0;
265 }
266 let arg = unsafe { value_pop(pc) };
267 if arg.is_null() {
268 return 0;
269 }
270 let b = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg).as_boolean();
271 crate::abi::exports_xml2::xmlXPathFreeObject(arg);
272 b as c_int
273}
274
275pub unsafe fn pop_string(pc: *mut XmlXPathParserContext) -> *mut xmlChar {
281 if pc.is_null() {
282 return ptr::null_mut();
283 }
284 let arg = unsafe { value_pop(pc) };
285 if arg.is_null() {
286 return ptr::null_mut();
287 }
288 let s = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg).as_string();
289 crate::abi::exports_xml2::xmlXPathFreeObject(arg);
290 crate::xml::string::xml_strndup(s.as_bytes().as_ptr() as *const xmlChar, s.len())
293}
294
295pub unsafe fn pop_node_set(pc: *mut XmlXPathParserContext) -> *mut _xmlNodeSet {
301 if pc.is_null() {
302 return ptr::null_mut();
303 }
304 let arg = unsafe { value_pop(pc) };
305 if arg.is_null() {
306 return ptr::null_mut();
307 }
308 let ns_ptr = unsafe { (*arg).nodesetval as *mut _xmlNodeSet };
309 unsafe { (*arg).nodesetval = ptr::null_mut() };
311 crate::abi::exports_xml2::xmlXPathFreeObject(arg);
312 ns_ptr
313}
314
315pub unsafe fn pop_external(pc: *mut XmlXPathParserContext) -> *mut c_void {
321 if pc.is_null() {
322 return ptr::null_mut();
323 }
324 let arg = unsafe { value_pop(pc) };
325 if arg.is_null() {
326 return ptr::null_mut();
327 }
328 let user = unsafe { (*arg).user };
329 crate::abi::exports_xml2::xmlXPathFreeObject(arg);
330 user
331}
332
333pub unsafe fn binary_number_op(pc: *mut XmlXPathParserContext, op: impl Fn(f64, f64) -> f64) {
342 let rhs = unsafe { pop_number(pc) };
343 let lhs = unsafe { pop_number(pc) };
344 let r = op(lhs, rhs);
345 unsafe { value_push(pc, new_number(r)) };
346}
347
348pub unsafe fn cast_top_to_number(pc: *mut XmlXPathParserContext) {
357 unsafe {
358 let val = (*pc).value;
359 if val.is_null() {
360 pc_set_error(pc, crate::abi::types::XPATH_INVALID_OPERAND as c_int);
361 return;
362 }
363 if (*val).type_ != xmlXPathObjectType::XPATH_NUMBER as c_int {
364 let v = crate::abi::exports_xml2::object_to_xpathvalue_pub(val);
365 (*val).floatval = v.as_number();
366 (*val).type_ = xmlXPathObjectType::XPATH_NUMBER as c_int;
367 }
368 }
369}
370
371fn node_set_pair_equal(a: &NodeSet, b: &NodeSet, neq: bool) -> bool {
374 if a.is_empty() || b.is_empty() {
375 return false;
376 }
377 if !neq {
380 for na in a.iter() {
381 if b.contains(na) {
382 return true;
383 }
384 }
385 }
386 for na in a.iter() {
387 let sa = node_string_value(na);
388 for nb in b.iter() {
389 let sb = node_string_value(nb);
390 if (sa == sb) ^ neq {
391 return true;
392 }
393 }
394 }
395 false
396}
397
398fn node_set_string_equal(a: &NodeSet, s: &str, neq: bool) -> bool {
400 if a.is_empty() {
401 return false;
402 }
403 for n in a.iter() {
404 let sv = node_string_value(n);
405 if sv == s {
406 if neq {
407 continue;
408 }
409 return true;
410 } else if neq {
411 return true;
412 }
413 }
414 false
415}
416
417fn node_set_number_equal(a: &NodeSet, f: f64, neq: bool) -> bool {
419 for n in a.iter() {
420 let sv = node_string_value(n);
421 let v = crate::xml::xpath::types::string_to_number(&sv);
422 if v.is_nan() {
423 if neq {
424 return true;
425 }
426 } else if (!neq && v == f) || (neq && v != f) {
427 return true;
428 }
429 }
430 false
431}
432
433pub fn equal_values_inner(v1: &XPathValue, v2: &XPathValue, neq: bool) -> bool {
437 match (v1, v2) {
440 (XPathValue::NodeSet(a), XPathValue::NodeSet(b)) => node_set_pair_equal(a, b, neq),
441 (XPathValue::NodeSet(a), other) => node_set_vs_value(a, other, neq),
442 (other, XPathValue::NodeSet(b)) => node_set_vs_value(b, other, neq),
443 _ => common_equal(v1, v2) ^ neq,
444 }
445}
446
447fn node_set_vs_value(ns: &NodeSet, other: &XPathValue, neq: bool) -> bool {
448 match other {
449 XPathValue::Boolean(b) => (!ns.is_empty()) == *b,
450 XPathValue::Number(f) => node_set_number_equal(ns, *f, neq),
451 XPathValue::String(s) => node_set_string_equal(ns, s, neq),
452 XPathValue::NodeSet(_) => unreachable!(),
454 }
455}
456
457fn common_equal(v1: &XPathValue, v2: &XPathValue) -> bool {
459 if matches!(v1, XPathValue::Boolean(_)) || matches!(v2, XPathValue::Boolean(_)) {
461 return v1.as_boolean() == v2.as_boolean();
462 }
463 if matches!(v1, XPathValue::Number(_)) || matches!(v2, XPathValue::Number(_)) {
466 return v1.as_number() == v2.as_number();
467 }
468 v1.as_string() == v2.as_string()
470}
471
472pub unsafe fn equal_values_impl(pc: *mut XmlXPathParserContext, neq: bool) -> c_int {
480 let arg2 = unsafe { value_pop(pc) };
481 if arg2.is_null() {
482 unsafe { (*pc).error = crate::abi::types::XPATH_INVALID_OPERAND as c_int };
483 return 0;
484 }
485 let arg1 = unsafe { value_pop(pc) };
486 if arg1.is_null() {
487 unsafe {
488 value_push(pc, arg2);
489 }
490 unsafe { (*pc).error = crate::abi::types::XPATH_INVALID_OPERAND as c_int };
491 return 0;
492 }
493 if arg1 == arg2 {
496 crate::abi::exports_xml2::xmlXPathFreeObject(arg1);
497 return if neq { 0 } else { 1 };
498 }
499 let v1 = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg1);
500 let v2 = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg2);
501 crate::abi::exports_xml2::xmlXPathFreeObject(arg1);
502 crate::abi::exports_xml2::xmlXPathFreeObject(arg2);
503
504 let eq = equal_values_inner(&v1, &v2, neq);
505 unsafe { value_push(pc, new_bool(eq)) };
506 eq as c_int
507}
508
509pub unsafe fn compare_values_impl(
516 pc: *mut XmlXPathParserContext,
517 inf: bool,
518 strict: bool,
519) -> c_int {
520 let arg2 = unsafe { value_pop(pc) };
521 if arg2.is_null() {
522 unsafe { (*pc).error = crate::abi::types::XPATH_INVALID_OPERAND as c_int };
523 return 0;
524 }
525 let arg1 = unsafe { value_pop(pc) };
526 if arg1.is_null() {
527 unsafe {
528 value_push(pc, arg2);
529 }
530 unsafe { (*pc).error = crate::abi::types::XPATH_INVALID_OPERAND as c_int };
531 return 0;
532 }
533
534 let v1 = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg1);
535 let v2 = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg2);
536 crate::abi::exports_xml2::xmlXPathFreeObject(arg1);
537 crate::abi::exports_xml2::xmlXPathFreeObject(arg2);
538
539 let (ns_side, other): (Option<&NodeSet>, Option<(&XPathValue, &XPathValue)>) = match (&v1, &v2)
540 {
541 (XPathValue::NodeSet(a), XPathValue::NodeSet(b)) => {
542 return compare_node_sets(a, b, inf, strict);
543 }
544 (XPathValue::NodeSet(a), _) => (Some(a), None),
545 (_, XPathValue::NodeSet(b)) => (Some(b), None),
546 _ => (None, Some((&v1, &v2))),
547 };
548
549 if let Some(ns) = ns_side {
550 let other = if matches!(&v1, XPathValue::NodeSet(_)) {
552 &v2
553 } else {
554 &v1
555 };
556 let (ns_is_left, scalar) = if matches!(&v1, XPathValue::NodeSet(_)) {
559 (true, other)
560 } else {
561 (false, other)
562 };
563 return compare_node_set_value(ns, scalar, ns_is_left, inf, strict);
564 }
565
566 let (l, r) = other.unwrap();
567 let a = l.as_number();
570 let b = r.as_number();
571 if a.is_nan() || b.is_nan() {
572 return 0;
573 }
574 let ret = if inf && strict {
575 a < b
576 } else if inf && !strict {
577 a <= b
578 } else if !inf && strict {
579 a > b
580 } else {
581 a >= b
582 };
583 ret as c_int
584}
585
586fn compare_node_sets(a: &NodeSet, b: &NodeSet, inf: bool, strict: bool) -> c_int {
587 if a.is_empty() || b.is_empty() {
588 return 0;
589 }
590 let b_nums: Vec<f64> = b.iter().map(|n| unsafe { node_to_number(n) }).collect();
591 for na in a.iter() {
592 let va = unsafe { node_to_number(na) };
593 if va.is_nan() {
594 continue;
595 }
596 for &vb in &b_nums {
597 if vb.is_nan() {
598 continue;
599 }
600 let ret = if inf && strict {
601 va < vb
602 } else if inf && !strict {
603 va <= vb
604 } else if !inf && strict {
605 va > vb
606 } else {
607 va >= vb
608 };
609 if ret {
610 return 1;
611 }
612 }
613 }
614 0
615}
616
617fn compare_node_set_value(
618 ns: &NodeSet,
619 scalar: &XPathValue,
620 ns_is_left: bool,
621 inf: bool,
622 strict: bool,
623) -> c_int {
624 match scalar {
625 XPathValue::Number(f) => compare_node_set_number(ns, *f, ns_is_left, inf, strict),
626 XPathValue::String(s) => compare_node_set_string(ns, s, ns_is_left, inf, strict),
627 XPathValue::Boolean(_) => {
628 let ns_bool = !ns.is_empty();
630 let b = scalar.as_boolean();
631 let (a, b) = if ns_is_left {
632 (ns_bool, b)
633 } else {
634 (b, ns_bool)
635 };
636 let ret = if inf && strict {
637 !a & b
638 } else if inf && !strict {
639 a <= b
640 } else if !inf && strict {
641 a & !b
642 } else {
643 a >= b
644 };
645 ret as c_int
646 }
647 _ => 0,
648 }
649}
650
651fn compare_node_set_number(
652 ns: &NodeSet,
653 f: f64,
654 ns_is_left: bool,
655 inf: bool,
656 strict: bool,
657) -> c_int {
658 for n in ns.iter() {
659 let v = unsafe { node_to_number(n) };
660 if v.is_nan() {
661 continue;
662 }
663 let (a, b) = if ns_is_left { (v, f) } else { (f, v) };
664 let ret = if inf && strict {
665 a < b
666 } else if inf && !strict {
667 a <= b
668 } else if !inf && strict {
669 a > b
670 } else {
671 a >= b
672 };
673 if ret {
674 return 1;
675 }
676 }
677 0
678}
679
680fn compare_node_set_string(
681 ns: &NodeSet,
682 s: &str,
683 ns_is_left: bool,
684 inf: bool,
685 strict: bool,
686) -> c_int {
687 for n in ns.iter() {
688 let sv = node_string_value(n);
689 let v = crate::xml::xpath::types::string_to_number(&sv);
690 let w = crate::xml::xpath::types::string_to_number(s);
691 if v.is_nan() || w.is_nan() {
692 continue;
693 }
694 let (a, b) = if ns_is_left { (v, w) } else { (w, 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
711unsafe fn node_to_number(node: *mut _xmlNode) -> f64 {
714 if node.is_null() {
715 return f64::NAN;
716 }
717 let sv = node_string_value(node);
718 crate::xml::xpath::types::string_to_number(&sv)
719}
720
721pub unsafe fn value_to_object(v: XPathValue) -> *mut _xmlXPathObject {
727 crate::abi::exports_xml2::xpath_to_object_pub(v)
728}
729
730#[allow(unused)]
731const fn _unused(_: *mut _xmlDoc) {}
732
733#[allow(unused)]
734const fn _unused_char(_: *const c_char) {}