1use rusty_xml_sax::{SaxAttr, SaxHandler};
4use rusty_xml_tree::{NodeId, NodeKind, XmlDoc};
5
6use crate::chvalid::{xml_is_char, xml_is_name_char, xml_is_name_start_char};
7use crate::error::*;
8
9pub const XML_PARSE_RECOVER: i32 = 1 << 0;
11pub const XML_PARSE_NOENT: i32 = 1 << 1;
12pub const XML_PARSE_DTDLOAD: i32 = 1 << 2;
13pub const XML_PARSE_DTDATTR: i32 = 1 << 3;
14pub const XML_PARSE_DTDVALID: i32 = 1 << 4;
15pub const XML_PARSE_NOERROR: i32 = 1 << 5;
16pub const XML_PARSE_NOWARNING: i32 = 1 << 6;
17pub const XML_PARSE_PEDANTIC: i32 = 1 << 7;
18pub const XML_PARSE_NOBLANKS: i32 = 1 << 8;
19pub const XML_PARSE_SAX1: i32 = 1 << 9;
20pub const XML_PARSE_XINCLUDE: i32 = 1 << 10;
21pub const XML_PARSE_NONET: i32 = 1 << 11;
22pub const XML_PARSE_NODICT: i32 = 1 << 12;
23pub const XML_PARSE_NSCLEAN: i32 = 1 << 13;
24pub const XML_PARSE_NOCDATA: i32 = 1 << 14;
25pub const XML_PARSE_NOXINCNODE: i32 = 1 << 15;
26pub const XML_PARSE_COMPACT: i32 = 1 << 16;
27pub const XML_PARSE_OLD10: i32 = 1 << 17;
28pub const XML_PARSE_NOBASEFIX: i32 = 1 << 18;
29pub const XML_PARSE_HUGE: i32 = 1 << 19;
30pub const XML_PARSE_OLDSAX: i32 = 1 << 20;
31pub const XML_PARSE_IGNORE_ENC: i32 = 1 << 21;
32pub const XML_PARSE_BIG_LINES: i32 = 1 << 22;
33pub const XML_PARSE_NO_XXE: i32 = 1 << 23;
34pub const XML_PARSE_UNZIP: i32 = 1 << 24;
35pub const XML_PARSE_NO_SYS_CATALOG: i32 = 1 << 25;
36pub const XML_PARSE_CATALOG_PI: i32 = 1 << 26;
37pub const XML_PARSE_SKIP_IDS: i32 = 1 << 27;
38
39const XML_NS: &str = "http://www.w3.org/XML/1998/namespace";
40const XMLNS_NS: &str = "http://www.w3.org/2000/xmlns/";
41
42const MAX_DEPTH: u32 = 256;
43const MAX_NAME: usize = 50_000;
44const MAX_TEXT: usize = 10_000_000;
45
46pub fn default_parse_options() -> i32 {
48 XML_PARSE_NONET | XML_PARSE_NO_XXE
49}
50
51#[doc(alias = "xmlInitParser")]
53pub fn xml_init_parser() {}
54
55#[doc(alias = "xmlCleanupParser")]
57pub fn xml_cleanup_parser() {}
58
59#[derive(Debug, Default)]
61pub struct XmlParserCtxt {
62 pub options: i32,
63 pub last_error: Option<XmlError>,
64 pub doc: Option<XmlDoc>,
65}
66
67#[doc(alias = "xmlNewParserCtxt")]
69pub fn xml_new_parser_ctxt() -> XmlParserCtxt {
70 XmlParserCtxt {
71 options: default_parse_options(),
72 last_error: None,
73 doc: None,
74 }
75}
76
77#[doc(alias = "xmlCtxtUseOptions")]
79pub fn xml_ctxt_use_options(ctxt: &mut XmlParserCtxt, options: i32) -> i32 {
80 ctxt.options = options | XML_PARSE_NONET | XML_PARSE_NO_XXE;
81 0
82}
83
84#[doc(alias = "xmlCtxtSetOptions")]
86pub fn xml_ctxt_set_options(ctxt: &mut XmlParserCtxt, options: i32) -> i32 {
87 xml_ctxt_use_options(ctxt, options)
88}
89
90#[doc(alias = "xmlCtxtGetOptions")]
92pub fn xml_ctxt_get_options(ctxt: &XmlParserCtxt) -> i32 {
93 ctxt.options
94}
95
96#[doc(alias = "xmlCtxtGetLastError")]
98pub fn xml_ctxt_get_last_error(ctxt: &XmlParserCtxt) -> Option<&XmlError> {
99 ctxt.last_error.as_ref()
100}
101
102#[doc(alias = "xmlCtxtGetDocument")]
104pub fn xml_ctxt_get_document(ctxt: &XmlParserCtxt) -> Option<&XmlDoc> {
105 ctxt.doc.as_ref()
106}
107
108#[doc(alias = "xmlReadMemory")]
110pub fn xml_read_memory(
111 buffer: &[u8],
112 url: Option<&str>,
113 encoding: Option<&str>,
114 options: i32,
115) -> Result<XmlDoc, XmlError> {
116 let mut rec = rusty_xml_sax::SaxRecorder::new();
117 parse_doc(buffer, url, encoding, options, &mut rec)
118}
119
120#[doc(alias = "xmlReadDoc")]
122pub fn xml_read_doc(
123 cur: &str,
124 url: Option<&str>,
125 encoding: Option<&str>,
126 options: i32,
127) -> Result<XmlDoc, XmlError> {
128 xml_read_memory(cur.as_bytes(), url, encoding, options)
129}
130
131#[doc(alias = "xmlReadFile")]
133pub fn xml_read_file(
134 filename: &str,
135 encoding: Option<&str>,
136 options: i32,
137) -> Result<XmlDoc, XmlError> {
138 let bytes = std::fs::read(filename).map_err(|e| {
139 XmlError::new(XML_ERR_DOCUMENT_START, e.to_string(), 0, 0)
140 })?;
141 xml_read_memory(&bytes, Some(filename), encoding, options)
142}
143
144#[doc(alias = "xmlCtxtReadMemory")]
146pub fn xml_ctxt_read_memory(
147 ctxt: &mut XmlParserCtxt,
148 buffer: &[u8],
149 url: Option<&str>,
150 encoding: Option<&str>,
151 options: i32,
152) -> Result<XmlDoc, XmlError> {
153 let opts = if options != 0 { options } else { ctxt.options };
154 match xml_read_memory(buffer, url, encoding, opts) {
155 Ok(doc) => {
156 ctxt.doc = Some(doc.clone());
157 ctxt.last_error = None;
158 Ok(doc)
159 }
160 Err(e) => {
161 ctxt.last_error = Some(e.clone());
162 Err(e)
163 }
164 }
165}
166
167pub fn xml_sax_parse_memory(
169 buffer: &[u8],
170 options: i32,
171 sax: &mut dyn SaxHandler,
172) -> Result<XmlDoc, XmlError> {
173 parse_doc(buffer, None, None, options, sax)
174}
175
176pub struct XmlPushParserCtxt {
178 buf: Vec<u8>,
179 options: i32,
180 url: Option<String>,
181 encoding: Option<String>,
182 doc: Option<XmlDoc>,
183 last_error: Option<XmlError>,
184}
185
186#[doc(alias = "xmlCreatePushParserCtxt")]
188pub fn xml_create_push_parser_ctxt(
189 chunk: &[u8],
190 url: Option<&str>,
191 encoding: Option<&str>,
192 options: i32,
193) -> XmlPushParserCtxt {
194 XmlPushParserCtxt {
195 buf: chunk.to_vec(),
196 options: options | XML_PARSE_NONET | XML_PARSE_NO_XXE,
197 url: url.map(str::to_string),
198 encoding: encoding.map(str::to_string),
199 doc: None,
200 last_error: None,
201 }
202}
203
204#[doc(alias = "xmlParseChunk")]
206pub fn xml_parse_chunk(
207 ctxt: &mut XmlPushParserCtxt,
208 chunk: &[u8],
209 terminate: i32,
210) -> Result<Option<XmlDoc>, XmlError> {
211 ctxt.buf.extend_from_slice(chunk);
212 if terminate == 0 {
213 return Ok(None);
214 }
215 match xml_read_memory(
216 &ctxt.buf,
217 ctxt.url.as_deref(),
218 ctxt.encoding.as_deref(),
219 ctxt.options,
220 ) {
221 Ok(doc) => {
222 ctxt.doc = Some(doc.clone());
223 Ok(Some(doc))
224 }
225 Err(e) => {
226 ctxt.last_error = Some(e.clone());
227 Err(e)
228 }
229 }
230}
231
232#[doc(alias = "xmlReadIO")]
234pub fn xml_read_io<F>(
235 mut read: F,
236 url: Option<&str>,
237 encoding: Option<&str>,
238 options: i32,
239) -> Result<XmlDoc, XmlError>
240where
241 F: FnMut(&mut [u8]) -> Result<usize, std::io::Error>,
242{
243 let mut buf = Vec::new();
244 let mut tmp = [0u8; 4096];
245 loop {
246 let n = read(&mut tmp).map_err(|e| XmlError::new(XML_ERR_DOCUMENT_START, e.to_string(), 0, 0))?;
247 if n == 0 {
248 break;
249 }
250 buf.extend_from_slice(&tmp[..n]);
251 }
252 xml_read_memory(&buf, url, encoding, options)
253}
254
255#[doc(alias = "xmlCtxtReset")]
257pub fn xml_ctxt_reset(ctxt: &mut XmlParserCtxt) {
258 ctxt.doc = None;
259 ctxt.last_error = None;
260}
261
262struct Parser<'a> {
263 input: &'a [u8],
264 pos: usize,
265 line: u32,
266 col: u32,
267 options: i32,
268 old10: bool,
269 depth: u32,
270 ns_stack: Vec<Vec<(Option<String>, String)>>,
271 sax: &'a mut dyn SaxHandler,
272 doc: XmlDoc,
273 stack: Vec<NodeId>,
274 char_buf: String,
275 started: bool,
276}
277
278impl<'a> Parser<'a> {
279 fn err(&self, code: i32, msg: impl Into<String>) -> XmlError {
280 XmlError::new(code, msg, self.line, self.col)
281 }
282
283 fn eof(&self) -> bool {
284 self.pos >= self.input.len()
285 }
286
287 fn peek_byte(&self) -> Option<u8> {
288 self.input.get(self.pos).copied()
289 }
290
291 fn starts_with(&self, s: &[u8]) -> bool {
292 self.input[self.pos..].starts_with(s)
293 }
294
295 fn bump_byte(&mut self) -> Option<u8> {
296 let b = self.peek_byte()?;
297 self.pos += 1;
298 if b == b'\n' {
299 self.line += 1;
300 self.col = 1;
301 } else {
302 self.col += 1;
303 }
304 Some(b)
305 }
306
307 fn peek_char(&self) -> Result<Option<char>, XmlError> {
309 if self.eof() {
310 return Ok(None);
311 }
312 let rest = &self.input[self.pos..];
313 if rest[0] == b'\r' {
314 return Ok(Some('\n'));
315 }
316 let s = std::str::from_utf8(rest).map_err(|_| {
317 XmlError::new(XML_ERR_INVALID_CHAR, "Invalid UTF-8", self.line, self.col)
318 })?;
319 Ok(s.chars().next())
320 }
321
322 fn bump_char(&mut self) -> Result<Option<char>, XmlError> {
323 let c = match self.peek_char()? {
324 None => return Ok(None),
325 Some(c) => c,
326 };
327 if self.peek_byte() == Some(b'\r') {
328 self.bump_byte();
329 if self.peek_byte() == Some(b'\n') {
330 self.bump_byte();
331 }
332 return Ok(Some('\n'));
333 }
334 let n = c.len_utf8();
335 for _ in 0..n {
336 self.bump_byte();
337 }
338 Ok(Some(c))
339 }
340
341 fn skip_s(&mut self) -> Result<(), XmlError> {
342 while let Some(c) = self.peek_char()? {
343 if crate::chvalid::xml_is_blank(c as u32) {
344 self.bump_char()?;
345 } else {
346 break;
347 }
348 }
349 Ok(())
350 }
351
352 fn expect_byte(&mut self, b: u8, code: i32, msg: &str) -> Result<(), XmlError> {
353 if self.peek_byte() != Some(b) {
354 return Err(self.err(code, msg));
355 }
356 self.bump_byte();
357 Ok(())
358 }
359
360 fn parse_name(&mut self) -> Result<String, XmlError> {
361 let c = self.peek_char()?.ok_or_else(|| self.err(XML_ERR_NAME_REQUIRED, "Name expected"))?;
362 if !xml_is_name_start_char(c as u32, self.old10) {
363 return Err(self.err(XML_ERR_NAME_REQUIRED, "Name expected"));
364 }
365 let mut s = String::new();
366 s.push(self.bump_char()?.unwrap());
367 while let Some(c) = self.peek_char()? {
368 if xml_is_name_char(c as u32, self.old10) {
369 if s.len() >= MAX_NAME && (self.options & XML_PARSE_HUGE) == 0 {
370 return Err(self.err(XML_ERR_NAME_REQUIRED, "Name too long"));
371 }
372 s.push(self.bump_char()?.unwrap());
373 } else {
374 break;
375 }
376 }
377 Ok(s)
378 }
379
380 fn split_qname(name: &str) -> Result<(Option<String>, String), XmlError> {
381 let mut parts = name.split(':');
382 let a = parts.next().unwrap();
383 match parts.next() {
384 None => Ok((None, a.to_string())),
385 Some(b) => {
386 if parts.next().is_some() || a.is_empty() || b.is_empty() {
387 return Err(XmlError::new(
388 XML_NS_ERR_QNAME,
389 format!("Invalid QName {name}"),
390 0,
391 0,
392 ));
393 }
394 Ok((Some(a.to_string()), b.to_string()))
395 }
396 }
397 }
398
399 fn lookup_ns(&self, prefix: Option<&str>) -> Option<String> {
400 if prefix == Some("xml") {
401 return Some(XML_NS.into());
402 }
403 if prefix == Some("xmlns") {
404 return Some(XMLNS_NS.into());
405 }
406 for frame in self.ns_stack.iter().rev() {
407 for (p, uri) in frame.iter().rev() {
408 if p.as_deref() == prefix {
409 return Some(uri.clone());
410 }
411 }
412 }
413 None
414 }
415
416 fn uri_has_scheme(uri: &str) -> bool {
417 let bytes = uri.as_bytes();
418 if bytes.is_empty() {
419 return false;
420 }
421 if !bytes[0].is_ascii_alphabetic() {
422 return false;
423 }
424 let mut i = 1;
425 while i < bytes.len() {
426 let b = bytes[i];
427 if b == b':' {
428 return true;
429 }
430 if b.is_ascii_alphanumeric() || b == b'+' || b == b'-' || b == b'.' {
431 i += 1;
432 } else {
433 return false;
434 }
435 }
436 false
437 }
438
439 fn flush_chars(&mut self, parent: Option<NodeId>) -> Result<(), XmlError> {
440 if self.char_buf.is_empty() {
441 return Ok(());
442 }
443 if self.char_buf.len() > MAX_TEXT && (self.options & XML_PARSE_HUGE) == 0 {
444 return Err(self.err(XML_ERR_INVALID_CHAR, "Text too long"));
445 }
446 let skip_blank = (self.options & XML_PARSE_NOBLANKS) != 0
447 && self.char_buf.chars().all(|c| crate::chvalid::xml_is_blank(c as u32));
448 if !skip_blank {
449 self.sax.characters(&self.char_buf);
450 if let Some(p) = parent {
451 let t = self.doc.alloc(NodeKind::Text, "#text");
452 self.doc.node_mut(t).content = self.char_buf.clone();
453 self.doc.xml_add_child(p, t);
454 }
455 }
456 self.char_buf.clear();
457 Ok(())
458 }
459
460 fn parse_comment(&mut self, parent: Option<NodeId>) -> Result<(), XmlError> {
461 let mut body = String::new();
463 loop {
464 if self.starts_with(b"-->") {
465 self.pos += 3;
466 self.col += 3;
467 break;
468 }
469 if self.eof() {
470 return Err(self.err(XML_ERR_COMMENT_NOT_FINISHED, "Comment not finished"));
471 }
472 if self.starts_with(b"--") {
473 return Err(self.err(XML_ERR_HYPHEN_IN_COMMENT, "Double hyphen in comment"));
474 }
475 let c = self.bump_char()?.unwrap();
476 if !xml_is_char(c as u32) {
477 return Err(self.err(XML_ERR_INVALID_CHAR, "Invalid character"));
478 }
479 body.push(c);
480 }
481 self.sax.comment(&body);
482 if let Some(p) = parent {
483 let n = self.doc.alloc(NodeKind::Comment, "#comment");
484 self.doc.node_mut(n).content = body;
485 self.doc.xml_add_child(p, n);
486 }
487 Ok(())
488 }
489
490 fn parse_pi(&mut self, parent: Option<NodeId>, xml_decl_ok: bool) -> Result<bool, XmlError> {
491 let target = self.parse_name()?;
493 if target.eq_ignore_ascii_case("xml") {
494 if xml_decl_ok {
495 return self.parse_xml_decl_rest().map(|_| true);
496 }
497 return Err(self.err(XML_ERR_RESERVED_XML_NAME, "Reserved PI target xml"));
498 }
499 let data = if matches!(self.peek_char()?, Some(c) if crate::chvalid::xml_is_blank(c as u32)) {
500 self.skip_s()?;
501 let mut d = String::new();
502 loop {
503 if self.starts_with(b"?>") {
504 self.pos += 2;
505 self.col += 2;
506 break;
507 }
508 if self.eof() {
509 return Err(self.err(XML_ERR_PI_NOT_FINISHED, "PI not finished"));
510 }
511 d.push(self.bump_char()?.unwrap());
512 }
513 Some(d)
514 } else {
515 if !self.starts_with(b"?>") {
516 return Err(self.err(XML_ERR_PI_NOT_FINISHED, "PI not finished"));
517 }
518 self.pos += 2;
519 self.col += 2;
520 None
521 };
522 self.sax.processing_instruction(&target, data.as_deref());
523 if let Some(p) = parent {
524 let n = self.doc.alloc(NodeKind::Pi, target);
525 self.doc.node_mut(n).content = data.unwrap_or_default();
526 self.doc.xml_add_child(p, n);
527 }
528 Ok(false)
529 }
530
531 fn parse_xml_decl_rest(&mut self) -> Result<(), XmlError> {
532 self.skip_s()?;
533 if !self.starts_with(b"version") {
535 return Err(self.err(XML_ERR_XMLDECL_NOT_FINISHED, "XML declaration version required"));
536 }
537 self.pos += 7;
538 self.col += 7;
539 self.skip_s()?;
540 self.expect_byte(b'=', XML_ERR_EQUAL_REQUIRED, "'=' required")?;
541 self.skip_s()?;
542 let ver = self.parse_quoted()?;
543 self.doc.version = ver;
544 self.skip_s()?;
545 if self.starts_with(b"encoding") {
546 self.pos += 8;
547 self.col += 8;
548 self.skip_s()?;
549 self.expect_byte(b'=', XML_ERR_EQUAL_REQUIRED, "'=' required")?;
550 self.skip_s()?;
551 let enc = self.parse_quoted()?;
552 self.doc.encoding = Some(enc);
553 self.skip_s()?;
554 }
555 if self.starts_with(b"standalone") {
556 self.pos += 10;
557 self.col += 10;
558 self.skip_s()?;
559 self.expect_byte(b'=', XML_ERR_EQUAL_REQUIRED, "'=' required")?;
560 self.skip_s()?;
561 let st = self.parse_quoted()?;
562 self.doc.standalone = match st.as_str() {
563 "yes" => Some(true),
564 "no" => Some(false),
565 _ => return Err(self.err(XML_ERR_XMLDECL_NOT_FINISHED, "standalone must be yes or no")),
566 };
567 self.skip_s()?;
568 }
569 if !self.starts_with(b"?>") {
570 return Err(self.err(XML_ERR_XMLDECL_NOT_FINISHED, "XML declaration not finished"));
571 }
572 self.pos += 2;
573 self.col += 2;
574 Ok(())
575 }
576
577 fn parse_quoted(&mut self) -> Result<String, XmlError> {
578 let q = self.peek_byte().ok_or_else(|| self.err(XML_ERR_LITERAL_NOT_FINISHED, "Quote expected"))?;
579 if q != b'\'' && q != b'"' {
580 return Err(self.err(XML_ERR_LITERAL_NOT_FINISHED, "Quote expected"));
581 }
582 self.bump_byte();
583 let mut s = String::new();
584 loop {
585 let c = self.bump_char()?.ok_or_else(|| self.err(XML_ERR_LITERAL_NOT_FINISHED, "Unterminated literal"))?;
586 if c as u8 == q && c.is_ascii() {
587 break;
588 }
589 s.push(c);
590 }
591 Ok(s)
592 }
593
594 fn parse_cdata(&mut self, parent: Option<NodeId>) -> Result<(), XmlError> {
595 let mut body = String::new();
597 loop {
598 if self.starts_with(b"]]>") {
599 self.pos += 3;
600 self.col += 3;
601 break;
602 }
603 if self.eof() {
604 return Err(self.err(XML_ERR_CDATA_NOT_FINISHED, "CDATA not finished"));
605 }
606 body.push(self.bump_char()?.unwrap());
607 }
608 if (self.options & XML_PARSE_NOCDATA) != 0 {
609 self.sax.characters(&body);
610 if let Some(p) = parent {
611 let t = self.doc.alloc(NodeKind::Text, "#text");
612 self.doc.node_mut(t).content = body;
613 self.doc.xml_add_child(p, t);
614 }
615 } else {
616 self.sax.cdata_block(&body);
617 if let Some(p) = parent {
618 let t = self.doc.alloc(NodeKind::CData, "#cdata-section");
619 self.doc.node_mut(t).content = body;
620 self.doc.xml_add_child(p, t);
621 }
622 }
623 Ok(())
624 }
625
626 fn parse_reference(&mut self) -> Result<String, XmlError> {
627 self.expect_byte(b'&', XML_ERR_ENTITYREF_NO_NAME, "& expected")?;
628 if self.peek_byte() == Some(b'#') {
629 self.bump_byte();
630 let hex = self.peek_byte() == Some(b'x') || self.peek_byte() == Some(b'X');
631 if hex {
632 self.bump_byte();
633 }
634 let mut digits = String::new();
635 while let Some(b) = self.peek_byte() {
636 let ok = if hex {
637 b.is_ascii_hexdigit()
638 } else {
639 b.is_ascii_digit()
640 };
641 if !ok {
642 break;
643 }
644 digits.push(b as char);
645 self.bump_byte();
646 }
647 if digits.is_empty() {
648 return Err(self.err(
649 if hex { XML_ERR_INVALID_HEX_CHARREF } else { XML_ERR_INVALID_DEC_CHARREF },
650 "Invalid character reference",
651 ));
652 }
653 self.expect_byte(b';', XML_ERR_ENTITYREF_SEMICOL_MISSING, "';' required")?;
654 let val = if hex {
655 u32::from_str_radix(&digits, 16).map_err(|_| {
656 self.err(XML_ERR_INVALID_HEX_CHARREF, "Invalid hex charref")
657 })?
658 } else {
659 digits.parse::<u32>().map_err(|_| {
660 self.err(XML_ERR_INVALID_DEC_CHARREF, "Invalid decimal charref")
661 })?
662 };
663 if !xml_is_char(val) {
664 return Err(self.err(XML_ERR_INVALID_CHARREF, "Invalid character reference"));
665 }
666 return Ok(char::from_u32(val).unwrap().to_string());
667 }
668 let name = self.parse_name()?;
669 self.expect_byte(b';', XML_ERR_ENTITYREF_SEMICOL_MISSING, "';' required")?;
670 match name.as_str() {
671 "lt" => Ok("<".into()),
672 "gt" => Ok(">".into()),
673 "amp" => Ok("&".into()),
674 "apos" => Ok("'".into()),
675 "quot" => Ok("\"".into()),
676 _ => {
677 if let Some(dtd) = &self.doc.dtd {
678 if let Some(repl) = dtd.entities.get(&name) {
679 return Ok(repl.clone());
680 }
681 }
682 Err(self.err(
683 XML_ERR_UNDECLARED_ENTITY,
684 format!("Entity '{name}' not defined"),
685 ))
686 }
687 }
688 }
689
690 fn parse_att_value(&mut self) -> Result<(String, usize), XmlError> {
691 let q = self.peek_byte().ok_or_else(|| {
692 self.err(XML_ERR_ATTRIBUTE_WITHOUT_VALUE, "Attribute value expected")
693 })?;
694 if q != b'\'' && q != b'"' {
695 return Err(self.err(XML_ERR_ATTRIBUTE_WITHOUT_VALUE, "Attribute value expected"));
696 }
697 self.bump_byte();
698 let start = self.pos;
699 let mut val = String::new();
700 loop {
701 if self.peek_byte() == Some(q) {
702 self.bump_byte();
703 break;
704 }
705 if self.eof() {
706 return Err(self.err(XML_ERR_LITERAL_NOT_FINISHED, "Unterminated attribute"));
707 }
708 if self.peek_byte() == Some(b'<') {
709 return Err(self.err(XML_ERR_LT_IN_ATTRIBUTE, "'<' in attribute value"));
710 }
711 if self.peek_byte() == Some(b'&') {
712 val.push_str(&self.parse_reference()?);
713 continue;
714 }
715 let c = self.bump_char()?.unwrap();
716 if c == '\n' || c == '\t' {
718 val.push(' ');
719 } else {
720 val.push(c);
721 }
722 }
723 Ok((val, start))
724 }
725
726 fn skip_doctype(&mut self) -> Result<(), XmlError> {
727 self.skip_s()?;
729 let name = self.parse_name()?;
730 self.skip_s()?;
731 let mut public_id = None;
732 let mut system_id = None;
733 if self.starts_with(b"SYSTEM") {
734 self.pos += 6;
735 self.col += 6;
736 self.skip_s()?;
737 system_id = Some(self.parse_quoted()?);
738 } else if self.starts_with(b"PUBLIC") {
739 self.pos += 6;
740 self.col += 6;
741 self.skip_s()?;
742 public_id = Some(self.parse_quoted()?);
743 self.skip_s()?;
744 system_id = Some(self.parse_quoted()?);
745 }
746 self.skip_s()?;
747 let mut int_subset = None;
748 if self.peek_byte() == Some(b'[') {
749 self.bump_byte();
750 let start = self.pos;
751 let mut depth = 1i32;
752 let mut in_quote: Option<u8> = None;
753 while depth > 0 {
754 let b = self.bump_byte().ok_or_else(|| {
755 self.err(XML_ERR_DOCUMENT_END, "Unterminated DOCTYPE")
756 })?;
757 if let Some(q) = in_quote {
758 if b == q {
759 in_quote = None;
760 }
761 continue;
762 }
763 match b {
764 b'\'' | b'"' => in_quote = Some(b),
765 b'[' => depth += 1,
766 b']' => depth -= 1,
767 _ => {}
768 }
769 }
770 int_subset = Some(String::from_utf8_lossy(&self.input[start..self.pos.saturating_sub(1)]).into_owned());
772 }
773 self.skip_s()?;
774 self.expect_byte(b'>', XML_ERR_GT_REQUIRED, "'>' required")?;
775 let mut dtd = if let Some(ref subset) = int_subset {
776 crate::dtd::parse_dtd_subset(subset).unwrap_or_default()
777 } else {
778 rusty_xml_tree::XmlDtd::default()
779 };
780 dtd.name = Some(name);
781 dtd.public_id = public_id;
782 dtd.system_id = system_id;
783 dtd.int_subset = int_subset;
784 self.doc.dtd = Some(dtd);
785 Ok(())
786 }
787
788 fn parse_element(&mut self, parent: NodeId) -> Result<(), XmlError> {
789 self.depth += 1;
790 if self.depth > MAX_DEPTH && (self.options & XML_PARSE_HUGE) == 0 {
791 return Err(self.err(XML_ERR_INTERNAL_ERROR, "Excessive element nesting"));
792 }
793 self.expect_byte(b'<', XML_ERR_LT_REQUIRED, "'<' required")?;
794 let qname = self.parse_name()?;
795 let (prefix, local) = Self::split_qname(&qname).map_err(|mut e| {
796 e.line = self.line;
797 e.col = self.col;
798 e
799 })?;
800
801 struct RawAttr {
802 qname: String,
803 value: String,
804 value_off: usize,
805 }
806 let mut raw_attrs: Vec<RawAttr> = Vec::new();
807 loop {
808 self.skip_s()?;
809 if self.starts_with(b"/>") || self.peek_byte() == Some(b'>') {
810 break;
811 }
812 let an = self.parse_name()?;
813 self.skip_s()?;
814 self.expect_byte(b'=', XML_ERR_EQUAL_REQUIRED, "'=' required")?;
815 self.skip_s()?;
816 let (value, value_off) = self.parse_att_value()?;
817 raw_attrs.push(RawAttr {
818 qname: an,
819 value,
820 value_off,
821 });
822 }
823 let empty = if self.starts_with(b"/>") {
824 self.pos += 2;
825 self.col += 2;
826 true
827 } else {
828 self.expect_byte(b'>', XML_ERR_GT_REQUIRED, "'>' required")?;
829 false
830 };
831
832 let mut ns_frame: Vec<(Option<String>, String)> = Vec::new();
833 for a in &raw_attrs {
834 let (ap, al) = Self::split_qname(&a.qname).map_err(|mut e| {
835 e.line = self.line;
836 e.col = self.col;
837 e
838 })?;
839 if ap.is_none() && al == "xmlns" {
840 if !a.value.is_empty() && !Self::uri_has_scheme(&a.value) {
841 let msg = format!("xmlns: URI {} is not absolute\n", a.value);
842 self.sax.warning(&msg);
843 }
844 ns_frame.push((None, a.value.clone()));
845 } else if ap.as_deref() == Some("xmlns") {
846 if !a.value.is_empty()
847 && !Self::uri_has_scheme(&a.value)
848 && (self.options & XML_PARSE_PEDANTIC) != 0
849 {
850 let msg = format!("xmlns:{}: URI {} is not absolute\n", al, a.value);
851 self.sax.warning(&msg);
852 }
853 ns_frame.push((Some(al), a.value.clone()));
854 }
855 }
856 self.ns_stack.push(ns_frame.clone());
857
858 let elem_uri = self.lookup_ns(prefix.as_deref());
859 if prefix.is_some() && elem_uri.is_none() {
860 return Err(self.err(
861 XML_NS_ERR_UNDEFINED_NAMESPACE,
862 format!("Undefined namespace prefix {}", prefix.as_deref().unwrap()),
863 ));
864 }
865
866 let mut sax_attrs: Vec<SaxAttr> = Vec::new();
867 let mut seen: Vec<(Option<String>, String)> = Vec::new();
868 for a in &raw_attrs {
869 let (ap, al) = Self::split_qname(&a.qname).unwrap();
870 if (ap.is_none() && al == "xmlns") || ap.as_deref() == Some("xmlns") {
871 continue;
872 }
873 let uri = if ap.is_some() {
874 let u = self.lookup_ns(ap.as_deref());
875 if u.is_none() {
876 return Err(self.err(
877 XML_NS_ERR_UNDEFINED_NAMESPACE,
878 format!("Undefined namespace prefix {}", ap.as_deref().unwrap()),
879 ));
880 }
881 u
882 } else {
883 None
884 };
885 let key = (uri.clone(), al.clone());
886 if seen.iter().any(|s| s == &key) {
887 return Err(self.err(XML_ERR_ATTRIBUTE_REDEFINED, "Attribute redefined"));
888 }
889 seen.push(key);
890 sax_attrs.push(SaxAttr {
891 local: al,
892 prefix: ap,
893 uri,
894 value: a.value.clone(),
895 value_input_off: Some(a.value_off),
896 });
897 }
898
899 self.sax.start_element_ns(
900 &local,
901 prefix.as_deref(),
902 elem_uri.as_deref(),
903 &ns_frame,
904 &sax_attrs,
905 0,
906 );
907
908 let elem = self.doc.alloc(NodeKind::Element, local.clone());
909 self.doc.node_mut(elem).prefix = prefix.clone();
910 self.doc.node_mut(elem).ns_uri = elem_uri.clone();
911 for (p, u) in &ns_frame {
912 self.doc.push_ns_def(elem, p.clone(), u.clone());
913 }
914 for a in &sax_attrs {
915 let aid = self.doc.add_attr(elem, &a.local, a.prefix.as_deref(), &a.value);
916 self.doc.node_mut(aid).ns_uri = a.uri.clone();
917 }
918 self.doc.xml_add_child(parent, elem);
919
920 if empty {
921 self.sax.end_element_ns(&local, prefix.as_deref(), elem_uri.as_deref());
922 self.ns_stack.pop();
923 self.depth -= 1;
924 return Ok(());
925 }
926
927 self.stack.push(elem);
928 self.parse_content(elem)?;
929 if !self.starts_with(b"</") {
930 return Err(self.err(
931 XML_ERR_TAG_NOT_FINISHED,
932 format!("Premature end of data in tag {local}"),
933 ));
934 }
935 self.pos += 2;
936 self.col += 2;
937 let end_name = self.parse_name()?;
938 self.skip_s()?;
939 self.expect_byte(b'>', XML_ERR_GT_REQUIRED, "'>' required")?;
940 if end_name != qname {
941 return Err(self.err(
942 XML_ERR_TAG_NAME_MISMATCH,
943 format!("Opening and ending tag mismatch: {qname} and {end_name}"),
944 ));
945 }
946 self.sax.end_element_ns(&local, prefix.as_deref(), elem_uri.as_deref());
947 self.ns_stack.pop();
948 self.stack.pop();
949 self.depth -= 1;
950 Ok(())
951 }
952
953 fn parse_content(&mut self, parent: NodeId) -> Result<(), XmlError> {
954 loop {
955 if self.eof() {
956 self.flush_chars(Some(parent))?;
957 return Ok(());
958 }
959 if self.starts_with(b"</") {
960 self.flush_chars(Some(parent))?;
961 return Ok(());
962 }
963 if self.starts_with(b"<!--") {
964 self.flush_chars(Some(parent))?;
965 self.pos += 4;
966 self.col += 4;
967 self.parse_comment(Some(parent))?;
968 continue;
969 }
970 if self.starts_with(b"<![CDATA[") {
971 self.flush_chars(Some(parent))?;
972 self.pos += 9;
973 self.col += 9;
974 self.parse_cdata(Some(parent))?;
975 continue;
976 }
977 if self.starts_with(b"<?") {
978 self.flush_chars(Some(parent))?;
979 self.pos += 2;
980 self.col += 2;
981 self.parse_pi(Some(parent), false)?;
982 continue;
983 }
984 if self.peek_byte() == Some(b'<') {
985 self.flush_chars(Some(parent))?;
986 self.parse_element(parent)?;
987 continue;
988 }
989 if self.peek_byte() == Some(b'&') {
990 self.flush_chars(Some(parent))?;
991 let repl = self.parse_reference()?;
992 self.char_buf.push_str(&repl);
993 self.flush_chars(Some(parent))?;
994 continue;
995 }
996 if self.starts_with(b"]]>") {
997 return Err(self.err(XML_ERR_MISPLACED_CDATA_END, "Misplaced CDATA end"));
998 }
999 let c = self.bump_char()?.unwrap();
1000 if !xml_is_char(c as u32) {
1001 return Err(self.err(XML_ERR_INVALID_CHAR, "Invalid character"));
1002 }
1003 self.char_buf.push(c);
1004 }
1005 }
1006
1007 fn parse_misc(&mut self, parent: NodeId) -> Result<(), XmlError> {
1008 loop {
1009 self.skip_s()?;
1010 if self.starts_with(b"<!--") {
1011 self.pos += 4;
1012 self.col += 4;
1013 self.parse_comment(Some(parent))?;
1014 continue;
1015 }
1016 if self.starts_with(b"<?") {
1017 self.pos += 2;
1018 self.col += 2;
1019 self.parse_pi(Some(parent), false)?;
1020 continue;
1021 }
1022 break;
1023 }
1024 Ok(())
1025 }
1026
1027 fn parse_document(&mut self) -> Result<(), XmlError> {
1028 if self.starts_with(&[0xef, 0xbb, 0xbf]) {
1029 self.pos += 3;
1030 }
1031 self.sax.set_document_locator();
1032 self.sax.start_document();
1033 self.started = true;
1034
1035 if self.starts_with(b"<?xml") {
1037 let save_pos = self.pos;
1038 let save_col = self.col;
1039 let save_line = self.line;
1040 self.pos += 5;
1041 self.col += 5;
1042 match self.peek_char()? {
1043 Some(c) if crate::chvalid::xml_is_blank(c as u32) => {
1044 self.parse_xml_decl_rest()?;
1045 }
1046 _ => {
1047 self.pos = save_pos;
1048 self.col = save_col;
1049 self.line = save_line;
1050 self.pos += 2;
1051 self.col += 2;
1052 self.parse_pi(Some(NodeId::DOCUMENT), false)?;
1053 }
1054 }
1055 }
1056
1057 self.parse_misc(NodeId::DOCUMENT)?;
1058 if self.starts_with(b"<!DOCTYPE") {
1059 self.pos += 9;
1060 self.col += 9;
1061 self.skip_doctype()?;
1062 self.parse_misc(NodeId::DOCUMENT)?;
1063 }
1064
1065 if self.peek_byte() != Some(b'<') {
1066 return Err(self.err(XML_ERR_DOCUMENT_EMPTY, "Document is empty"));
1067 }
1068 self.parse_element(NodeId::DOCUMENT)?;
1069 self.parse_misc(NodeId::DOCUMENT)?;
1070 self.skip_s()?;
1071 if !self.eof() {
1072 return Err(self.err(XML_ERR_EXTRA_CONTENT, "Extra content at the end of the document"));
1073 }
1074 self.sax.end_document();
1075 Ok(())
1076 }
1077}
1078
1079fn parse_doc(
1080 buffer: &[u8],
1081 _url: Option<&str>,
1082 encoding: Option<&str>,
1083 options: i32,
1084 sax: &mut dyn SaxHandler,
1085) -> Result<XmlDoc, XmlError> {
1086 let (converted, enc_name) = crate::encoding::xml_convert_to_utf8(buffer, encoding)?;
1087 parse_utf8(&converted, enc_name.as_deref(), options, sax)
1088}
1089
1090fn parse_utf8(
1091 buffer: &[u8],
1092 enc_name: Option<&str>,
1093 options: i32,
1094 sax: &mut dyn SaxHandler,
1095) -> Result<XmlDoc, XmlError> {
1096 let options = options | XML_PARSE_NONET | XML_PARSE_NO_XXE;
1097 let mut p = Parser {
1098 input: buffer,
1099 pos: 0,
1100 line: 1,
1101 col: 1,
1102 options,
1103 old10: (options & XML_PARSE_OLD10) != 0,
1104 depth: 0,
1105 ns_stack: Vec::new(),
1106 sax,
1107 doc: XmlDoc::xml_new_doc(Some("1.0")),
1108 stack: Vec::new(),
1109 char_buf: String::new(),
1110 started: false,
1111 };
1112 match p.parse_document() {
1113 Ok(()) => {
1114 apply_dtd_defaults(&mut p.doc);
1115 if p.doc.encoding.is_none() {
1116 if let Some(n) = enc_name {
1117 if !n.eq_ignore_ascii_case("UTF-8") && !n.eq_ignore_ascii_case("US-ASCII") {
1118 p.doc.encoding = Some(n.to_string());
1119 }
1120 }
1121 }
1122 Ok(p.doc)
1123 }
1124 Err(e) => {
1125 if p.started {
1126 p.sax.end_document();
1127 }
1128 Err(e)
1129 }
1130 }
1131}
1132
1133fn apply_dtd_defaults(doc: &mut XmlDoc) {
1134 let Some(dtd) = doc.dtd.clone() else { return };
1135 let n = doc.len();
1136 for i in 0..n {
1137 let id = NodeId(i as u32);
1138 if doc.kind(id) != NodeKind::Element {
1139 continue;
1140 }
1141 let name = doc.name(id).to_string();
1142 for ((elem, aname), ad) in &dtd.attributes {
1143 if elem != &name {
1144 continue;
1145 }
1146 if doc.xml_get_prop(id, aname).is_some() {
1147 continue;
1148 }
1149 if let Some(v) = &ad.default_value {
1150 doc.xml_set_prop(id, aname, v);
1151 }
1152 }
1153 }
1154}
1155
1156#[cfg(test)]
1157mod chvalid_tests {
1158 use crate::xml_is_char;
1159 use std::path::PathBuf;
1160
1161 #[test]
1162 fn xml_is_char_matches_c_bmp_dump() {
1163 let mut p = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
1164 p.pop();
1165 p.pop();
1166 p.push("corpora");
1167 p.push("xmlIsChar-bmp.bin");
1168 if !p.exists() {
1169 return;
1170 }
1171 let dump = std::fs::read(&p).expect("corpora/xmlIsChar-bmp.bin");
1172 assert_eq!(dump.len(), 65536);
1173 for i in 0u32..=0xffff {
1174 let want = dump[i as usize] != 0;
1175 let got = xml_is_char(i);
1176 assert_eq!(got, want, "xml_is_char({i:#x}) = {got}, C dump = {want}");
1177 }
1178 }
1179}