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::{xmlFree, xmlMalloc, 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 =
92 xmlMalloc(VALUE_TAB_SIZE * size_of::<*mut _xmlXPathObject>()) as *mut *mut _xmlXPathObject;
93 if tab.is_null() {
94 xmlFree(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 xmlFree(tab as *mut c_void);
132 }
133 xmlFree(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 {
209 let obj = xmlMallocZero(size_of::<_xmlXPathObject>()) as *mut _xmlXPathObject;
210 if !obj.is_null() {
211 unsafe {
212 (*obj).type_ = xmlXPathObjectType::XPATH_NUMBER as c_int;
213 (*obj).floatval = n;
214 }
215 }
216 obj
217}
218
219pub unsafe fn new_string(s: *const xmlChar) -> *mut _xmlXPathObject {
225 crate::xml::xpath::exports::xmlXPathNewString(s)
226}
227
228pub unsafe fn pop_number(pc: *mut XmlXPathParserContext) -> f64 {
235 if pc.is_null() {
236 return f64::NAN;
237 }
238 let arg = unsafe { value_pop(pc) };
239 if arg.is_null() {
240 return f64::NAN;
241 }
242 let n = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg).as_number();
243 crate::abi::exports_xml2::xmlXPathFreeObject(arg);
244 n
245}
246
247pub unsafe fn pop_boolean(pc: *mut XmlXPathParserContext) -> c_int {
253 if pc.is_null() {
254 return 0;
255 }
256 let arg = unsafe { value_pop(pc) };
257 if arg.is_null() {
258 return 0;
259 }
260 let b = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg).as_boolean();
261 crate::abi::exports_xml2::xmlXPathFreeObject(arg);
262 b as c_int
263}
264
265pub unsafe fn pop_string(pc: *mut XmlXPathParserContext) -> *mut xmlChar {
271 if pc.is_null() {
272 return ptr::null_mut();
273 }
274 let arg = unsafe { value_pop(pc) };
275 if arg.is_null() {
276 return ptr::null_mut();
277 }
278 let s = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg).as_string();
279 crate::abi::exports_xml2::xmlXPathFreeObject(arg);
280 crate::xml::string::xml_strdup(s.as_bytes().as_ptr() as *const xmlChar)
281}
282
283pub unsafe fn pop_node_set(pc: *mut XmlXPathParserContext) -> *mut _xmlNodeSet {
289 if pc.is_null() {
290 return ptr::null_mut();
291 }
292 let arg = unsafe { value_pop(pc) };
293 if arg.is_null() {
294 return ptr::null_mut();
295 }
296 let ns_ptr = unsafe { (*arg).nodesetval as *mut _xmlNodeSet };
297 unsafe { (*arg).nodesetval = ptr::null_mut() };
299 crate::abi::exports_xml2::xmlXPathFreeObject(arg);
300 ns_ptr
301}
302
303pub unsafe fn pop_external(pc: *mut XmlXPathParserContext) -> *mut c_void {
309 if pc.is_null() {
310 return ptr::null_mut();
311 }
312 let arg = unsafe { value_pop(pc) };
313 if arg.is_null() {
314 return ptr::null_mut();
315 }
316 let user = unsafe { (*arg).user };
317 crate::abi::exports_xml2::xmlXPathFreeObject(arg);
318 user
319}
320
321pub unsafe fn binary_number_op(pc: *mut XmlXPathParserContext, op: impl Fn(f64, f64) -> f64) {
330 let rhs = unsafe { pop_number(pc) };
331 let lhs = unsafe { pop_number(pc) };
332 let r = op(lhs, rhs);
333 unsafe { value_push(pc, new_number(r)) };
334}
335
336pub unsafe fn cast_top_to_number(pc: *mut XmlXPathParserContext) {
345 unsafe {
346 let val = (*pc).value;
347 if val.is_null() {
348 pc_set_error(pc, crate::abi::types::XPATH_INVALID_OPERAND as c_int);
349 return;
350 }
351 if (*val).type_ != xmlXPathObjectType::XPATH_NUMBER as c_int {
352 let v = crate::abi::exports_xml2::object_to_xpathvalue_pub(val);
353 (*val).floatval = v.as_number();
354 (*val).type_ = xmlXPathObjectType::XPATH_NUMBER as c_int;
355 }
356 }
357}
358
359fn node_set_pair_equal(a: &NodeSet, b: &NodeSet, neq: bool) -> bool {
362 if a.is_empty() || b.is_empty() {
363 return false;
364 }
365 if !neq {
368 for na in a.iter() {
369 if b.contains(na) {
370 return true;
371 }
372 }
373 }
374 for na in a.iter() {
375 let sa = node_string_value(na);
376 for nb in b.iter() {
377 let sb = node_string_value(nb);
378 if (sa == sb) ^ neq {
379 return true;
380 }
381 }
382 }
383 false
384}
385
386fn node_set_string_equal(a: &NodeSet, s: &str, neq: bool) -> bool {
388 if a.is_empty() {
389 return false;
390 }
391 for n in a.iter() {
392 let sv = node_string_value(n);
393 if sv == s {
394 if neq {
395 continue;
396 }
397 return true;
398 } else if neq {
399 return true;
400 }
401 }
402 false
403}
404
405fn node_set_number_equal(a: &NodeSet, f: f64, neq: bool) -> bool {
407 for n in a.iter() {
408 let sv = node_string_value(n);
409 let v = crate::xml::xpath::types::string_to_number(&sv);
410 if v.is_nan() {
411 if neq {
412 return true;
413 }
414 } else if (!neq && v == f) || (neq && v != f) {
415 return true;
416 }
417 }
418 false
419}
420
421pub fn equal_values_inner(v1: &XPathValue, v2: &XPathValue, neq: bool) -> bool {
425 match (v1, v2) {
428 (XPathValue::NodeSet(a), XPathValue::NodeSet(b)) => node_set_pair_equal(a, b, neq),
429 (XPathValue::NodeSet(a), other) => node_set_vs_value(a, other, neq),
430 (other, XPathValue::NodeSet(b)) => node_set_vs_value(b, other, neq),
431 _ => common_equal(v1, v2) ^ neq,
432 }
433}
434
435fn node_set_vs_value(ns: &NodeSet, other: &XPathValue, neq: bool) -> bool {
436 match other {
437 XPathValue::Boolean(b) => (!ns.is_empty()) == *b,
438 XPathValue::Number(f) => node_set_number_equal(ns, *f, neq),
439 XPathValue::String(s) => node_set_string_equal(ns, s, neq),
440 XPathValue::NodeSet(_) => unreachable!(),
442 }
443}
444
445fn common_equal(v1: &XPathValue, v2: &XPathValue) -> bool {
447 if matches!(v1, XPathValue::Boolean(_)) || matches!(v2, XPathValue::Boolean(_)) {
449 return v1.as_boolean() == v2.as_boolean();
450 }
451 if matches!(v1, XPathValue::Number(_)) || matches!(v2, XPathValue::Number(_)) {
454 return v1.as_number() == v2.as_number();
455 }
456 v1.as_string() == v2.as_string()
458}
459
460pub unsafe fn equal_values_impl(pc: *mut XmlXPathParserContext, neq: bool) -> c_int {
468 let arg2 = unsafe { value_pop(pc) };
469 if arg2.is_null() {
470 unsafe { (*pc).error = crate::abi::types::XPATH_INVALID_OPERAND as c_int };
471 return 0;
472 }
473 let arg1 = unsafe { value_pop(pc) };
474 if arg1.is_null() {
475 unsafe {
476 value_push(pc, arg2);
477 }
478 unsafe { (*pc).error = crate::abi::types::XPATH_INVALID_OPERAND as c_int };
479 return 0;
480 }
481 if arg1 == arg2 {
484 crate::abi::exports_xml2::xmlXPathFreeObject(arg1);
485 return if neq { 0 } else { 1 };
486 }
487 let v1 = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg1);
488 let v2 = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg2);
489 crate::abi::exports_xml2::xmlXPathFreeObject(arg1);
490 crate::abi::exports_xml2::xmlXPathFreeObject(arg2);
491
492 let eq = equal_values_inner(&v1, &v2, neq);
493 unsafe { value_push(pc, new_bool(eq)) };
494 eq as c_int
495}
496
497pub unsafe fn compare_values_impl(
504 pc: *mut XmlXPathParserContext,
505 inf: bool,
506 strict: bool,
507) -> c_int {
508 let arg2 = unsafe { value_pop(pc) };
509 if arg2.is_null() {
510 unsafe { (*pc).error = crate::abi::types::XPATH_INVALID_OPERAND as c_int };
511 return 0;
512 }
513 let arg1 = unsafe { value_pop(pc) };
514 if arg1.is_null() {
515 unsafe {
516 value_push(pc, arg2);
517 }
518 unsafe { (*pc).error = crate::abi::types::XPATH_INVALID_OPERAND as c_int };
519 return 0;
520 }
521
522 let v1 = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg1);
523 let v2 = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg2);
524 crate::abi::exports_xml2::xmlXPathFreeObject(arg1);
525 crate::abi::exports_xml2::xmlXPathFreeObject(arg2);
526
527 let (ns_side, other): (Option<&NodeSet>, Option<(&XPathValue, &XPathValue)>) = match (&v1, &v2)
528 {
529 (XPathValue::NodeSet(a), XPathValue::NodeSet(b)) => {
530 return compare_node_sets(a, b, inf, strict);
531 }
532 (XPathValue::NodeSet(a), _) => (Some(a), None),
533 (_, XPathValue::NodeSet(b)) => (Some(b), None),
534 _ => (None, Some((&v1, &v2))),
535 };
536
537 if let Some(ns) = ns_side {
538 let other = if matches!(&v1, XPathValue::NodeSet(_)) {
540 &v2
541 } else {
542 &v1
543 };
544 let (ns_is_left, scalar) = if matches!(&v1, XPathValue::NodeSet(_)) {
547 (true, other)
548 } else {
549 (false, other)
550 };
551 return compare_node_set_value(ns, scalar, ns_is_left, inf, strict);
552 }
553
554 let (l, r) = other.unwrap();
555 let a = l.as_number();
558 let b = r.as_number();
559 if a.is_nan() || b.is_nan() {
560 return 0;
561 }
562 let ret = if inf && strict {
563 a < b
564 } else if inf && !strict {
565 a <= b
566 } else if !inf && strict {
567 a > b
568 } else {
569 a >= b
570 };
571 ret as c_int
572}
573
574fn compare_node_sets(a: &NodeSet, b: &NodeSet, inf: bool, strict: bool) -> c_int {
575 if a.is_empty() || b.is_empty() {
576 return 0;
577 }
578 let b_nums: Vec<f64> = b.iter().map(|n| unsafe { node_to_number(n) }).collect();
579 for na in a.iter() {
580 let va = unsafe { node_to_number(na) };
581 if va.is_nan() {
582 continue;
583 }
584 for &vb in &b_nums {
585 if vb.is_nan() {
586 continue;
587 }
588 let ret = if inf && strict {
589 va < vb
590 } else if inf && !strict {
591 va <= vb
592 } else if !inf && strict {
593 va > vb
594 } else {
595 va >= vb
596 };
597 if ret {
598 return 1;
599 }
600 }
601 }
602 0
603}
604
605fn compare_node_set_value(
606 ns: &NodeSet,
607 scalar: &XPathValue,
608 ns_is_left: bool,
609 inf: bool,
610 strict: bool,
611) -> c_int {
612 match scalar {
613 XPathValue::Number(f) => compare_node_set_number(ns, *f, ns_is_left, inf, strict),
614 XPathValue::String(s) => compare_node_set_string(ns, s, ns_is_left, inf, strict),
615 XPathValue::Boolean(_) => {
616 let ns_bool = !ns.is_empty();
618 let b = scalar.as_boolean();
619 let (a, b) = if ns_is_left {
620 (ns_bool, b)
621 } else {
622 (b, ns_bool)
623 };
624 let ret = if inf && strict {
625 a < b
626 } else if inf && !strict {
627 a <= b
628 } else if !inf && strict {
629 a > b
630 } else {
631 a >= b
632 };
633 ret as c_int
634 }
635 _ => 0,
636 }
637}
638
639fn compare_node_set_number(
640 ns: &NodeSet,
641 f: f64,
642 ns_is_left: bool,
643 inf: bool,
644 strict: bool,
645) -> c_int {
646 for n in ns.iter() {
647 let v = unsafe { node_to_number(n) };
648 if v.is_nan() {
649 continue;
650 }
651 let (a, b) = if ns_is_left { (v, f) } else { (f, v) };
652 let ret = if inf && strict {
653 a < b
654 } else if inf && !strict {
655 a <= b
656 } else if !inf && strict {
657 a > b
658 } else {
659 a >= b
660 };
661 if ret {
662 return 1;
663 }
664 }
665 0
666}
667
668fn compare_node_set_string(
669 ns: &NodeSet,
670 s: &str,
671 ns_is_left: bool,
672 inf: bool,
673 strict: bool,
674) -> c_int {
675 for n in ns.iter() {
676 let sv = node_string_value(n);
677 let v = crate::xml::xpath::types::string_to_number(&sv);
678 let w = crate::xml::xpath::types::string_to_number(s);
679 if v.is_nan() || w.is_nan() {
680 continue;
681 }
682 let (a, b) = if ns_is_left { (v, w) } else { (w, v) };
683 let ret = if inf && strict {
684 a < b
685 } else if inf && !strict {
686 a <= b
687 } else if !inf && strict {
688 a > b
689 } else {
690 a >= b
691 };
692 if ret {
693 return 1;
694 }
695 }
696 0
697}
698
699unsafe fn node_to_number(node: *mut _xmlNode) -> f64 {
702 if node.is_null() {
703 return f64::NAN;
704 }
705 let sv = node_string_value(node);
706 crate::xml::xpath::types::string_to_number(&sv)
707}
708
709pub unsafe fn value_to_object(v: XPathValue) -> *mut _xmlXPathObject {
715 crate::abi::exports_xml2::xpath_to_object_pub(v)
716}
717
718#[allow(unused)]
719fn _unused(_: *mut _xmlDoc) {}
720
721#[allow(unused)]
722fn _unused_char(_: *const c_char) {}