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 {
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_strndup(s.as_bytes().as_ptr() as *const xmlChar, s.len())
283}
284
285pub unsafe fn pop_node_set(pc: *mut XmlXPathParserContext) -> *mut _xmlNodeSet {
291 if pc.is_null() {
292 return ptr::null_mut();
293 }
294 let arg = unsafe { value_pop(pc) };
295 if arg.is_null() {
296 return ptr::null_mut();
297 }
298 let ns_ptr = unsafe { (*arg).nodesetval as *mut _xmlNodeSet };
299 unsafe { (*arg).nodesetval = ptr::null_mut() };
301 crate::abi::exports_xml2::xmlXPathFreeObject(arg);
302 ns_ptr
303}
304
305pub unsafe fn pop_external(pc: *mut XmlXPathParserContext) -> *mut c_void {
311 if pc.is_null() {
312 return ptr::null_mut();
313 }
314 let arg = unsafe { value_pop(pc) };
315 if arg.is_null() {
316 return ptr::null_mut();
317 }
318 let user = unsafe { (*arg).user };
319 crate::abi::exports_xml2::xmlXPathFreeObject(arg);
320 user
321}
322
323pub unsafe fn binary_number_op(pc: *mut XmlXPathParserContext, op: impl Fn(f64, f64) -> f64) {
332 let rhs = unsafe { pop_number(pc) };
333 let lhs = unsafe { pop_number(pc) };
334 let r = op(lhs, rhs);
335 unsafe { value_push(pc, new_number(r)) };
336}
337
338pub unsafe fn cast_top_to_number(pc: *mut XmlXPathParserContext) {
347 unsafe {
348 let val = (*pc).value;
349 if val.is_null() {
350 pc_set_error(pc, crate::abi::types::XPATH_INVALID_OPERAND as c_int);
351 return;
352 }
353 if (*val).type_ != xmlXPathObjectType::XPATH_NUMBER as c_int {
354 let v = crate::abi::exports_xml2::object_to_xpathvalue_pub(val);
355 (*val).floatval = v.as_number();
356 (*val).type_ = xmlXPathObjectType::XPATH_NUMBER as c_int;
357 }
358 }
359}
360
361fn node_set_pair_equal(a: &NodeSet, b: &NodeSet, neq: bool) -> bool {
364 if a.is_empty() || b.is_empty() {
365 return false;
366 }
367 if !neq {
370 for na in a.iter() {
371 if b.contains(na) {
372 return true;
373 }
374 }
375 }
376 for na in a.iter() {
377 let sa = node_string_value(na);
378 for nb in b.iter() {
379 let sb = node_string_value(nb);
380 if (sa == sb) ^ neq {
381 return true;
382 }
383 }
384 }
385 false
386}
387
388fn node_set_string_equal(a: &NodeSet, s: &str, neq: bool) -> bool {
390 if a.is_empty() {
391 return false;
392 }
393 for n in a.iter() {
394 let sv = node_string_value(n);
395 if sv == s {
396 if neq {
397 continue;
398 }
399 return true;
400 } else if neq {
401 return true;
402 }
403 }
404 false
405}
406
407fn node_set_number_equal(a: &NodeSet, f: f64, neq: bool) -> bool {
409 for n in a.iter() {
410 let sv = node_string_value(n);
411 let v = crate::xml::xpath::types::string_to_number(&sv);
412 if v.is_nan() {
413 if neq {
414 return true;
415 }
416 } else if (!neq && v == f) || (neq && v != f) {
417 return true;
418 }
419 }
420 false
421}
422
423pub fn equal_values_inner(v1: &XPathValue, v2: &XPathValue, neq: bool) -> bool {
427 match (v1, v2) {
430 (XPathValue::NodeSet(a), XPathValue::NodeSet(b)) => node_set_pair_equal(a, b, neq),
431 (XPathValue::NodeSet(a), other) => node_set_vs_value(a, other, neq),
432 (other, XPathValue::NodeSet(b)) => node_set_vs_value(b, other, neq),
433 _ => common_equal(v1, v2) ^ neq,
434 }
435}
436
437fn node_set_vs_value(ns: &NodeSet, other: &XPathValue, neq: bool) -> bool {
438 match other {
439 XPathValue::Boolean(b) => (!ns.is_empty()) == *b,
440 XPathValue::Number(f) => node_set_number_equal(ns, *f, neq),
441 XPathValue::String(s) => node_set_string_equal(ns, s, neq),
442 XPathValue::NodeSet(_) => unreachable!(),
444 }
445}
446
447fn common_equal(v1: &XPathValue, v2: &XPathValue) -> bool {
449 if matches!(v1, XPathValue::Boolean(_)) || matches!(v2, XPathValue::Boolean(_)) {
451 return v1.as_boolean() == v2.as_boolean();
452 }
453 if matches!(v1, XPathValue::Number(_)) || matches!(v2, XPathValue::Number(_)) {
456 return v1.as_number() == v2.as_number();
457 }
458 v1.as_string() == v2.as_string()
460}
461
462pub unsafe fn equal_values_impl(pc: *mut XmlXPathParserContext, neq: bool) -> c_int {
470 let arg2 = unsafe { value_pop(pc) };
471 if arg2.is_null() {
472 unsafe { (*pc).error = crate::abi::types::XPATH_INVALID_OPERAND as c_int };
473 return 0;
474 }
475 let arg1 = unsafe { value_pop(pc) };
476 if arg1.is_null() {
477 unsafe {
478 value_push(pc, arg2);
479 }
480 unsafe { (*pc).error = crate::abi::types::XPATH_INVALID_OPERAND as c_int };
481 return 0;
482 }
483 if arg1 == arg2 {
486 crate::abi::exports_xml2::xmlXPathFreeObject(arg1);
487 return if neq { 0 } else { 1 };
488 }
489 let v1 = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg1);
490 let v2 = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg2);
491 crate::abi::exports_xml2::xmlXPathFreeObject(arg1);
492 crate::abi::exports_xml2::xmlXPathFreeObject(arg2);
493
494 let eq = equal_values_inner(&v1, &v2, neq);
495 unsafe { value_push(pc, new_bool(eq)) };
496 eq as c_int
497}
498
499pub unsafe fn compare_values_impl(
506 pc: *mut XmlXPathParserContext,
507 inf: bool,
508 strict: bool,
509) -> c_int {
510 let arg2 = unsafe { value_pop(pc) };
511 if arg2.is_null() {
512 unsafe { (*pc).error = crate::abi::types::XPATH_INVALID_OPERAND as c_int };
513 return 0;
514 }
515 let arg1 = unsafe { value_pop(pc) };
516 if arg1.is_null() {
517 unsafe {
518 value_push(pc, arg2);
519 }
520 unsafe { (*pc).error = crate::abi::types::XPATH_INVALID_OPERAND as c_int };
521 return 0;
522 }
523
524 let v1 = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg1);
525 let v2 = crate::abi::exports_xml2::object_to_xpathvalue_pub(arg2);
526 crate::abi::exports_xml2::xmlXPathFreeObject(arg1);
527 crate::abi::exports_xml2::xmlXPathFreeObject(arg2);
528
529 let (ns_side, other): (Option<&NodeSet>, Option<(&XPathValue, &XPathValue)>) = match (&v1, &v2)
530 {
531 (XPathValue::NodeSet(a), XPathValue::NodeSet(b)) => {
532 return compare_node_sets(a, b, inf, strict);
533 }
534 (XPathValue::NodeSet(a), _) => (Some(a), None),
535 (_, XPathValue::NodeSet(b)) => (Some(b), None),
536 _ => (None, Some((&v1, &v2))),
537 };
538
539 if let Some(ns) = ns_side {
540 let other = if matches!(&v1, XPathValue::NodeSet(_)) {
542 &v2
543 } else {
544 &v1
545 };
546 let (ns_is_left, scalar) = if matches!(&v1, XPathValue::NodeSet(_)) {
549 (true, other)
550 } else {
551 (false, other)
552 };
553 return compare_node_set_value(ns, scalar, ns_is_left, inf, strict);
554 }
555
556 let (l, r) = other.unwrap();
557 let a = l.as_number();
560 let b = r.as_number();
561 if a.is_nan() || b.is_nan() {
562 return 0;
563 }
564 let ret = if inf && strict {
565 a < b
566 } else if inf && !strict {
567 a <= b
568 } else if !inf && strict {
569 a > b
570 } else {
571 a >= b
572 };
573 ret as c_int
574}
575
576fn compare_node_sets(a: &NodeSet, b: &NodeSet, inf: bool, strict: bool) -> c_int {
577 if a.is_empty() || b.is_empty() {
578 return 0;
579 }
580 let b_nums: Vec<f64> = b.iter().map(|n| unsafe { node_to_number(n) }).collect();
581 for na in a.iter() {
582 let va = unsafe { node_to_number(na) };
583 if va.is_nan() {
584 continue;
585 }
586 for &vb in &b_nums {
587 if vb.is_nan() {
588 continue;
589 }
590 let ret = if inf && strict {
591 va < vb
592 } else if inf && !strict {
593 va <= vb
594 } else if !inf && strict {
595 va > vb
596 } else {
597 va >= vb
598 };
599 if ret {
600 return 1;
601 }
602 }
603 }
604 0
605}
606
607fn compare_node_set_value(
608 ns: &NodeSet,
609 scalar: &XPathValue,
610 ns_is_left: bool,
611 inf: bool,
612 strict: bool,
613) -> c_int {
614 match scalar {
615 XPathValue::Number(f) => compare_node_set_number(ns, *f, ns_is_left, inf, strict),
616 XPathValue::String(s) => compare_node_set_string(ns, s, ns_is_left, inf, strict),
617 XPathValue::Boolean(_) => {
618 let ns_bool = !ns.is_empty();
620 let b = scalar.as_boolean();
621 let (a, b) = if ns_is_left {
622 (ns_bool, b)
623 } else {
624 (b, ns_bool)
625 };
626 let ret = if inf && strict {
627 a < b
628 } else if inf && !strict {
629 a <= b
630 } else if !inf && strict {
631 a > b
632 } else {
633 a >= b
634 };
635 ret as c_int
636 }
637 _ => 0,
638 }
639}
640
641fn compare_node_set_number(
642 ns: &NodeSet,
643 f: f64,
644 ns_is_left: bool,
645 inf: bool,
646 strict: bool,
647) -> c_int {
648 for n in ns.iter() {
649 let v = unsafe { node_to_number(n) };
650 if v.is_nan() {
651 continue;
652 }
653 let (a, b) = if ns_is_left { (v, f) } else { (f, v) };
654 let ret = if inf && strict {
655 a < b
656 } else if inf && !strict {
657 a <= b
658 } else if !inf && strict {
659 a > b
660 } else {
661 a >= b
662 };
663 if ret {
664 return 1;
665 }
666 }
667 0
668}
669
670fn compare_node_set_string(
671 ns: &NodeSet,
672 s: &str,
673 ns_is_left: bool,
674 inf: bool,
675 strict: bool,
676) -> c_int {
677 for n in ns.iter() {
678 let sv = node_string_value(n);
679 let v = crate::xml::xpath::types::string_to_number(&sv);
680 let w = crate::xml::xpath::types::string_to_number(s);
681 if v.is_nan() || w.is_nan() {
682 continue;
683 }
684 let (a, b) = if ns_is_left { (v, w) } else { (w, v) };
685 let ret = if inf && strict {
686 a < b
687 } else if inf && !strict {
688 a <= b
689 } else if !inf && strict {
690 a > b
691 } else {
692 a >= b
693 };
694 if ret {
695 return 1;
696 }
697 }
698 0
699}
700
701unsafe fn node_to_number(node: *mut _xmlNode) -> f64 {
704 if node.is_null() {
705 return f64::NAN;
706 }
707 let sv = node_string_value(node);
708 crate::xml::xpath::types::string_to_number(&sv)
709}
710
711pub unsafe fn value_to_object(v: XPathValue) -> *mut _xmlXPathObject {
717 crate::abi::exports_xml2::xpath_to_object_pub(v)
718}
719
720#[allow(unused)]
721fn _unused(_: *mut _xmlDoc) {}
722
723#[allow(unused)]
724fn _unused_char(_: *const c_char) {}