1#![allow(
51 missing_docs,
52 missing_debug_implementations,
53 non_snake_case,
54 non_camel_case_types,
55 non_upper_case_globals
56)]
57
58use core::ffi::c_void;
59use core::ptr;
60use std::os::raw::{c_char, c_int};
61
62use crate::abi::allocator::{xmlFreeImpl, xmlMallocImpl, xmlMallocZero};
63use crate::abi::exports_string::xmlStrstr;
64use crate::abi::exports_tree::{xmlNodeGetBase, xmlNodeSetBase};
65use crate::abi::exports_uri::xmlCanonicPath;
66use crate::abi::exports_xml2::{
67 xmlReadFile, xmlSaveFile, xmlStrEqual, xmlStrchr, xmlStrdup, xmlValidateDocument,
68 xmlValidateDtd, xmlXPathEval, xmlXPathFreeContext, xmlXPathFreeObject, xmlXPathNewContext,
69 xmlXPathRegisterNs,
70};
71use crate::abi::structs::{
72 _xmlAttr, _xmlDoc, _xmlDtd, _xmlNode, _xmlNodeSet, _xmlValidCtxt, _xmlXPathContext,
73 _xmlXPathObject,
74};
75use crate::abi::types::{xmlChar, xmlElementType, xmlXPathObjectType};
76use crate::xml::xpath::exports::xmlXPathDebugDumpObject;
77use crate::xml::{debug, io, tree};
78
79pub type xmlShellReadlineFunc = Option<unsafe extern "C" fn(prompt: *mut c_char) -> *mut c_char>;
86
87#[repr(C)]
89pub struct _xmlShellCtxt {
90 pub filename: *mut c_char,
92 pub doc: *mut _xmlDoc,
94 pub node: *mut _xmlNode,
96 pub pctxt: *mut _xmlXPathContext,
98 pub loaded: c_int,
100 pub output: *mut c_void,
102 pub input: xmlShellReadlineFunc,
104}
105
106pub type xmlShellCmd = Option<
109 unsafe extern "C" fn(*mut _xmlShellCtxt, *mut c_char, *mut _xmlNode, *mut _xmlNode) -> c_int,
110>;
111
112extern "C" {
113 fn fwrite(ptr: *const c_void, size: usize, nmemb: usize, stream: *mut c_void) -> usize;
115 fn fputs(s: *const c_char, stream: *mut c_void) -> c_int;
117 static mut stdout: *mut c_void;
119 static mut stderr: *mut c_void;
121}
122
123unsafe fn push_cstr(v: &mut Vec<u8>, s: *const c_char) {
129 if s.is_null() {
130 return;
131 }
132 let len = libc::strlen(s);
133 v.extend_from_slice(core::slice::from_raw_parts(s as *const u8, len));
134}
135
136unsafe fn out_bytes(fp: *mut c_void, bytes: &[u8]) {
138 if fp.is_null() || bytes.is_empty() {
139 return;
140 }
141 unsafe {
142 fwrite(bytes.as_ptr() as *const c_void, 1, bytes.len(), fp);
143 }
144}
145
146unsafe fn out_cstr(fp: *mut c_void, s: *const c_char) {
148 if fp.is_null() || s.is_null() {
149 return;
150 }
151 unsafe {
152 fputs(s, fp);
153 }
154}
155
156unsafe fn shell_generic_error(arg: *const c_char, mid: &[u8], end: &[u8]) {
162 let mut v = Vec::new();
163 unsafe {
164 push_cstr(&mut v, arg);
165 }
166 v.extend_from_slice(mid);
167 v.extend_from_slice(end);
168 unsafe {
169 out_bytes(stderr, &v);
170 }
171}
172
173unsafe fn shell_elem_dump(fp: *mut c_void, doc: *mut _xmlDoc, node: *mut _xmlNode) -> c_int {
177 if fp.is_null() || node.is_null() {
178 return -1;
179 }
180 let buf = io::buf_create(-1);
181 if buf.is_null() {
182 return -1;
183 }
184 let ret = tree::node_dump(buf, doc, node, 0, 0);
185 if ret < 0 {
186 io::buf_free(buf);
187 return -1;
188 }
189 let content = io::buf_content(buf);
190 let len = io::buf_length(buf);
191 if !content.is_null() && len > 0 {
192 unsafe {
193 fwrite(content as *const c_void, 1, len as usize, fp);
194 }
195 }
196 io::buf_free(buf);
197 0
198}
199
200unsafe fn shell_html_doc_dump(fp: *mut c_void, doc: *mut _xmlDoc) -> c_int {
202 if fp.is_null() || doc.is_null() {
203 return -1;
204 }
205 let buf = io::buf_create(-1);
206 if buf.is_null() {
207 return -1;
208 }
209 let ret = crate::xml::html::doc_dump(buf, doc);
210 if ret < 0 {
211 io::buf_free(buf);
212 return -1;
213 }
214 let content = io::buf_content(buf);
215 let len = io::buf_length(buf);
216 if !content.is_null() && len > 0 {
217 unsafe {
218 fwrite(content as *const c_void, 1, len as usize, fp);
219 }
220 }
221 io::buf_free(buf);
222 ret
223}
224
225unsafe fn shell_html_node_dump_file(fp: *mut c_void, node: *mut _xmlNode) -> c_int {
227 if fp.is_null() || node.is_null() {
228 return -1;
229 }
230 let buf = io::buf_create(-1);
231 if buf.is_null() {
232 return -1;
233 }
234 let before = io::buf_length(buf);
235 crate::xml::html::serialize_node(node, buf, 0, 0);
236 let after = io::buf_length(buf);
237 if after < 0 || before < 0 {
238 io::buf_free(buf);
239 return -1;
240 }
241 let content = io::buf_content(buf);
242 let len = io::buf_length(buf);
243 if !content.is_null() && len > 0 {
244 unsafe {
245 fwrite(content as *const c_void, 1, len as usize, fp);
246 }
247 }
248 io::buf_free(buf);
249 after - before
250}
251
252unsafe fn shell_get_node_path(node: *const _xmlNode) -> *mut xmlChar {
255 if node.is_null() || (*node).type_ == xmlElementType::XML_NAMESPACE_DECL as c_int {
256 return ptr::null_mut();
257 }
258
259 let mut segments: Vec<Vec<u8>> = Vec::new();
262 let mut cur: *const _xmlNode = node;
263
264 loop {
265 if cur.is_null() {
266 break;
267 }
268 let typ = (*cur).type_;
269 let mut seg: Vec<u8> = Vec::new();
270 let mut occur: c_int = 0;
271 let mut generic: bool;
272 let next: *const _xmlNode;
273
274 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
275 || typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
276 {
277 if !segments.is_empty() {
280 break;
281 }
282 seg.extend_from_slice(b"/");
283 next = ptr::null();
284 } else if typ == xmlElementType::XML_ELEMENT_NODE as c_int {
285 generic = false;
286 seg.extend_from_slice(b"/");
287 let name = (*cur).name;
288 let ns = (*cur).ns;
289 if !name.is_null() {
290 if !ns.is_null() && !(*ns).prefix.is_null() {
291 unsafe {
292 push_cstr(&mut seg, (*ns).prefix as *const c_char);
293 }
294 seg.push(b':');
295 unsafe {
296 push_cstr(&mut seg, name as *const c_char);
297 }
298 } else if !ns.is_null() {
299 generic = true;
301 seg.extend_from_slice(b"*");
302 } else {
303 unsafe {
304 push_cstr(&mut seg, name as *const c_char);
305 }
306 }
307 }
308 next = (*cur).parent;
309
310 let mut tmp = (*cur).prev;
312 while !tmp.is_null() {
313 if (*tmp).type_ == xmlElementType::XML_ELEMENT_NODE as c_int
314 && (generic || unsafe { shell_same_element_name(cur, tmp) })
315 {
316 occur += 1;
317 }
318 tmp = (*tmp).prev;
319 }
320 if occur == 0 {
321 let mut tmp = (*cur).next;
322 while !tmp.is_null() && occur == 0 {
323 if (*tmp).type_ == xmlElementType::XML_ELEMENT_NODE as c_int
324 && (generic || unsafe { shell_same_element_name(cur, tmp) })
325 {
326 occur += 1;
327 }
328 tmp = (*tmp).next;
329 }
330 if occur != 0 {
331 occur = 1;
332 }
333 } else {
334 occur += 1;
335 }
336 } else if typ == xmlElementType::XML_COMMENT_NODE as c_int {
337 seg.extend_from_slice(b"/comment()");
338 next = (*cur).parent;
339
340 let mut tmp = (*cur).prev;
341 while !tmp.is_null() {
342 if (*tmp).type_ == xmlElementType::XML_COMMENT_NODE as c_int {
343 occur += 1;
344 }
345 tmp = (*tmp).prev;
346 }
347 if occur == 0 {
348 let mut tmp = (*cur).next;
349 while !tmp.is_null() && occur == 0 {
350 if (*tmp).type_ == xmlElementType::XML_COMMENT_NODE as c_int {
351 occur += 1;
352 }
353 tmp = (*tmp).next;
354 }
355 if occur != 0 {
356 occur = 1;
357 }
358 } else {
359 occur += 1;
360 }
361 } else if typ == xmlElementType::XML_TEXT_NODE as c_int
362 || typ == xmlElementType::XML_CDATA_SECTION_NODE as c_int
363 {
364 seg.extend_from_slice(b"/text()");
365 next = (*cur).parent;
366
367 let mut tmp = (*cur).prev;
368 while !tmp.is_null() {
369 if (*tmp).type_ == xmlElementType::XML_TEXT_NODE as c_int
370 || (*tmp).type_ == xmlElementType::XML_CDATA_SECTION_NODE as c_int
371 {
372 occur += 1;
373 }
374 tmp = (*tmp).prev;
375 }
376 if occur == 0 {
377 let mut tmp = (*cur).next;
378 while !tmp.is_null() {
379 if (*tmp).type_ == xmlElementType::XML_TEXT_NODE as c_int
380 || (*tmp).type_ == xmlElementType::XML_CDATA_SECTION_NODE as c_int
381 {
382 occur = 1;
383 break;
384 }
385 tmp = (*tmp).next;
386 }
387 } else {
388 occur += 1;
389 }
390 } else if typ == xmlElementType::XML_PI_NODE as c_int {
391 let mut nm = Vec::new();
392 nm.extend_from_slice(b"processing-instruction('");
393 unsafe {
394 push_cstr(&mut nm, (*cur).name as *const c_char);
395 }
396 nm.extend_from_slice(b"')");
397 seg.extend_from_slice(b"/");
398 seg.extend_from_slice(&nm);
399 next = (*cur).parent;
400
401 let mut tmp = (*cur).prev;
402 while !tmp.is_null() {
403 if (*tmp).type_ == xmlElementType::XML_PI_NODE as c_int
404 && unsafe { xmlStrEqual((*cur).name, (*tmp).name) != 0 }
405 {
406 occur += 1;
407 }
408 tmp = (*tmp).prev;
409 }
410 if occur == 0 {
411 let mut tmp = (*cur).next;
412 while !tmp.is_null() && occur == 0 {
413 if (*tmp).type_ == xmlElementType::XML_PI_NODE as c_int
414 && unsafe { xmlStrEqual((*cur).name, (*tmp).name) != 0 }
415 {
416 occur += 1;
417 }
418 tmp = (*tmp).next;
419 }
420 if occur != 0 {
421 occur = 1;
422 }
423 } else {
424 occur += 1;
425 }
426 } else if typ == xmlElementType::XML_ATTRIBUTE_NODE as c_int {
427 seg.extend_from_slice(b"/@");
428 let attr = cur as *const _xmlAttr;
429 let name = (*attr).name;
430 if !name.is_null() {
431 if !(*attr).ns.is_null() && !(*(*attr).ns).prefix.is_null() {
432 unsafe {
433 push_cstr(&mut seg, (*(*attr).ns).prefix as *const c_char);
434 }
435 seg.push(b':');
436 }
437 unsafe {
438 push_cstr(&mut seg, name as *const c_char);
439 }
440 }
441 next = (*attr).parent;
442 } else {
443 return ptr::null_mut();
444 }
445
446 if occur != 0 {
447 seg.extend_from_slice(format!("[{}]", occur).as_bytes());
448 }
449 segments.push(seg);
450 if next.is_null() {
451 break;
452 }
453 cur = next;
454 }
455
456 let mut path: Vec<u8> = Vec::new();
458 for seg in segments.iter().rev() {
459 path.extend_from_slice(seg);
460 }
461 path.push(0);
462
463 let ret = xmlMallocImpl(path.len()) as *mut xmlChar;
464 if ret.is_null() {
465 return ptr::null_mut();
466 }
467 unsafe {
468 ptr::copy_nonoverlapping(path.as_ptr(), ret, path.len());
469 }
470 ret
471}
472
473unsafe fn shell_same_element_name(a: *const _xmlNode, b: *const _xmlNode) -> bool {
476 unsafe {
477 if xmlStrEqual((*a).name, (*b).name) == 0 {
478 return false;
479 }
480 let ans = (*a).ns;
481 let bns = (*b).ns;
482 if ans == bns {
483 return true;
484 }
485 if !ans.is_null() && !bns.is_null() {
486 return xmlStrEqual((*ans).prefix, (*bns).prefix) != 0;
487 }
488 false
489 }
490}
491
492#[no_mangle]
500pub unsafe extern "C" fn xmlShellPrintXPathError(errorType: c_int, arg: *const c_char) {
501 let default_arg = b"Result\0";
502 let arg = if arg.is_null() {
503 default_arg.as_ptr() as *const c_char
504 } else {
505 arg
506 };
507
508 if errorType == xmlXPathObjectType::XPATH_UNDEFINED as c_int {
509 unsafe { shell_generic_error(arg, b": ", b"no such node\n") };
510 } else if errorType == xmlXPathObjectType::XPATH_BOOLEAN as c_int {
511 unsafe { shell_generic_error(arg, b" is a Boolean", b"\n") };
512 } else if errorType == xmlXPathObjectType::XPATH_NUMBER as c_int {
513 unsafe { shell_generic_error(arg, b" is a number", b"\n") };
514 } else if errorType == xmlXPathObjectType::XPATH_STRING as c_int {
515 unsafe { shell_generic_error(arg, b" is a string", b"\n") };
516 } else if errorType == xmlXPathObjectType::XPATH_POINT as c_int {
517 unsafe { shell_generic_error(arg, b" is a point", b"\n") };
518 } else if errorType == xmlXPathObjectType::XPATH_RANGE as c_int {
519 unsafe { shell_generic_error(arg, b" is a range", b"\n") };
520 } else if errorType == xmlXPathObjectType::XPATH_LOCATIONSET as c_int {
521 unsafe { shell_generic_error(arg, b" is a range", b"\n") };
522 } else if errorType == xmlXPathObjectType::XPATH_USERS as c_int {
523 unsafe { shell_generic_error(arg, b" is user-defined", b"\n") };
524 } else if errorType == xmlXPathObjectType::XPATH_XSLT_TREE as c_int {
525 unsafe { shell_generic_error(arg, b" is an XSLT value tree", b"\n") };
526 }
527}
528
529unsafe fn xmlShellPrintNodeCtxt(ctxt: *mut _xmlShellCtxt, node: *mut _xmlNode) {
532 if node.is_null() {
533 return;
534 }
535 let fp = if ctxt.is_null() {
536 unsafe { stdout }
537 } else {
538 (*ctxt).output
539 };
540
541 let typ = unsafe { (*node).type_ };
542 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int {
543 tree::xmlDocDump(fp, node as *mut _xmlDoc);
544 } else if typ == xmlElementType::XML_ATTRIBUTE_NODE as c_int {
545 unsafe {
546 debug::xmlDebugDumpAttrList(fp as *mut debug::_IO_FILE, node as *mut _xmlAttr, 0);
547 }
548 } else {
549 unsafe {
550 shell_elem_dump(fp, (*node).doc, node);
551 }
552 }
553 unsafe {
554 out_bytes(fp, b"\n");
555 }
556}
557
558#[no_mangle]
560pub unsafe extern "C" fn xmlShellPrintNode(node: *mut _xmlNode) {
561 unsafe { xmlShellPrintNodeCtxt(ptr::null_mut(), node) };
562}
563
564#[no_mangle]
569pub unsafe extern "C" fn xmlShellPrintXPathResult(list: *mut _xmlXPathObject) {
570 unsafe {
571 xmlXPathDebugDumpObject(stdout, list, 0);
572 }
573}
574
575#[no_mangle]
582pub unsafe extern "C" fn xmlShellList(
583 ctxt: *mut _xmlShellCtxt,
584 _arg: *mut c_char,
585 node: *mut _xmlNode,
586 _node2: *mut _xmlNode,
587) -> c_int {
588 if ctxt.is_null() {
589 return 0;
590 }
591 if node.is_null() {
592 unsafe {
593 out_bytes((*ctxt).output, b"NULL\n");
594 }
595 return 0;
596 }
597 let typ = unsafe { (*node).type_ };
598 let mut cur: *mut _xmlNode;
599 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
600 || typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
601 {
602 cur = unsafe { (*(node as *mut _xmlDoc)).children };
603 } else if typ == xmlElementType::XML_NAMESPACE_DECL as c_int {
604 unsafe {
605 debug::xmlLsOneNode((*ctxt).output as *mut debug::_IO_FILE, node);
606 }
607 return 0;
608 } else if !unsafe { (*node).children }.is_null() {
609 cur = unsafe { (*node).children };
610 } else {
611 unsafe {
612 debug::xmlLsOneNode((*ctxt).output as *mut debug::_IO_FILE, node);
613 }
614 return 0;
615 }
616 while !cur.is_null() {
617 unsafe {
618 debug::xmlLsOneNode((*ctxt).output as *mut debug::_IO_FILE, cur);
619 cur = (*cur).next;
620 }
621 }
622 0
623}
624
625#[no_mangle]
628pub unsafe extern "C" fn xmlShellBase(
629 ctxt: *mut _xmlShellCtxt,
630 _arg: *mut c_char,
631 node: *mut _xmlNode,
632 _node2: *mut _xmlNode,
633) -> c_int {
634 if ctxt.is_null() {
635 return 0;
636 }
637 if node.is_null() {
638 unsafe {
639 out_bytes((*ctxt).output, b"NULL\n");
640 }
641 return 0;
642 }
643
644 let base = unsafe { xmlNodeGetBase((*node).doc, node) };
645
646 if base.is_null() {
647 unsafe {
648 out_bytes((*ctxt).output, b" No base found !!!\n");
649 }
650 } else {
651 unsafe {
652 out_cstr((*ctxt).output, base as *const c_char);
653 out_bytes((*ctxt).output, b"\n");
654 xmlFreeImpl(base as *mut c_void);
655 }
656 }
657 0
658}
659
660#[no_mangle]
663pub unsafe extern "C" fn xmlShellDir(
664 ctxt: *mut _xmlShellCtxt,
665 _arg: *mut c_char,
666 node: *mut _xmlNode,
667 _node2: *mut _xmlNode,
668) -> c_int {
669 if ctxt.is_null() {
670 return 0;
671 }
672 if node.is_null() {
673 unsafe {
674 out_bytes((*ctxt).output, b"NULL\n");
675 }
676 return 0;
677 }
678 let typ = unsafe { (*node).type_ };
679 unsafe {
680 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
681 || typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
682 {
683 debug::xmlDebugDumpDocumentHead(
684 (*ctxt).output as *mut debug::_IO_FILE,
685 node as *mut _xmlDoc,
686 );
687 } else if typ == xmlElementType::XML_ATTRIBUTE_NODE as c_int {
688 debug::xmlDebugDumpAttr(
689 (*ctxt).output as *mut debug::_IO_FILE,
690 node as *mut _xmlAttr,
691 0,
692 );
693 } else {
694 debug::xmlDebugDumpOneNode((*ctxt).output as *mut debug::_IO_FILE, node, 0);
695 }
696 }
697 0
698}
699
700#[no_mangle]
708pub unsafe extern "C" fn xmlShellCat(
709 ctxt: *mut _xmlShellCtxt,
710 _arg: *mut c_char,
711 node: *mut _xmlNode,
712 _node2: *mut _xmlNode,
713) -> c_int {
714 if ctxt.is_null() {
715 return 0;
716 }
717 if node.is_null() {
718 unsafe {
719 out_bytes((*ctxt).output, b"NULL\n");
720 }
721 return 0;
722 }
723 let out = unsafe { (*ctxt).output };
724 let is_html =
725 unsafe { (*(*ctxt).doc).type_ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int };
726 let typ = unsafe { (*node).type_ };
727 unsafe {
728 if is_html {
729 if typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int {
730 shell_html_doc_dump(out, node as *mut _xmlDoc);
731 } else {
732 shell_html_node_dump_file(out, node);
733 }
734 } else if typ == xmlElementType::XML_DOCUMENT_NODE as c_int {
735 tree::xmlDocDump(out, node as *mut _xmlDoc);
736 } else {
737 shell_elem_dump(out, (*ctxt).doc, node);
738 }
739 out_bytes(out, b"\n");
740 }
741 0
742}
743
744#[no_mangle]
747pub unsafe extern "C" fn xmlShellLoad(
748 ctxt: *mut _xmlShellCtxt,
749 filename: *mut c_char,
750 _node: *mut _xmlNode,
751 _node2: *mut _xmlNode,
752) -> c_int {
753 if ctxt.is_null() || filename.is_null() {
754 return -1;
755 }
756 let mut html = 0;
757 if !unsafe { (*ctxt).doc }.is_null() {
758 html = unsafe {
759 ((*(*ctxt).doc).type_ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int) as c_int
760 };
761 }
762
763 let doc: *mut _xmlDoc = if html != 0 {
764 unsafe { crate::xml::html::parse_file(filename, ptr::null()) }
767 } else {
768 unsafe { xmlReadFile(filename, ptr::null(), 0) }
769 };
770
771 if !doc.is_null() {
772 unsafe {
773 if (*ctxt).loaded == 1 {
774 tree::free_doc((*ctxt).doc);
775 }
776 (*ctxt).loaded = 1;
777 xmlXPathFreeContext((*ctxt).pctxt);
778 if !(*ctxt).filename.is_null() {
779 xmlFreeImpl((*ctxt).filename as *mut c_void);
780 }
781 (*ctxt).doc = doc;
782 (*ctxt).node = doc as *mut _xmlNode;
783 (*ctxt).pctxt = xmlXPathNewContext(doc);
784 (*ctxt).filename = xmlCanonicPath(filename) as *mut c_char;
785 }
786 0
787 } else {
788 -1
789 }
790}
791
792#[no_mangle]
796pub unsafe extern "C" fn xmlShellWrite(
797 ctxt: *mut _xmlShellCtxt,
798 filename: *mut c_char,
799 node: *mut _xmlNode,
800 _node2: *mut _xmlNode,
801) -> c_int {
802 if node.is_null() {
803 return -1;
804 }
805 if filename.is_null() || *filename == 0 {
806 unsafe {
807 shell_generic_error(
808 b"Write command requires a filename argument\n\0".as_ptr() as *const c_char,
809 b"",
810 b"",
811 );
812 }
813 return -1;
814 }
815 if libc::access(filename, libc::W_OK) != 0 {
817 unsafe {
818 shell_generic_error(b"Cannot write to \0".as_ptr() as *const c_char, b"", b"");
819 shell_generic_error(filename, b"", b"\n");
820 }
821 return -1;
822 }
823 let typ = unsafe { (*node).type_ };
824 unsafe {
825 match typ {
826 t if t == xmlElementType::XML_DOCUMENT_NODE as c_int => {
827 if xmlSaveFile(filename, (*ctxt).doc) < -1 {
828 shell_generic_error(
829 b"Failed to write to \0".as_ptr() as *const c_char,
830 b"",
831 b"",
832 );
833 shell_generic_error(filename, b"", b"\n");
834 return -1;
835 }
836 }
837 t if t == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int => {
838 if shell_save_html_doc(filename, (*ctxt).doc) < 0 {
840 shell_generic_error(
841 b"Failed to write to \0".as_ptr() as *const c_char,
842 b"",
843 b"",
844 );
845 shell_generic_error(filename, b"", b"\n");
846 return -1;
847 }
848 }
849 _ => {
850 let f = libc::fopen(filename, b"w\0".as_ptr() as *const c_char);
851 if f.is_null() {
852 shell_generic_error(
853 b"Failed to write to \0".as_ptr() as *const c_char,
854 b"",
855 b"",
856 );
857 shell_generic_error(filename, b"", b"\n");
858 return -1;
859 }
860 shell_elem_dump(f as *mut c_void, (*ctxt).doc, node);
861 libc::fclose(f);
862 }
863 }
864 }
865 0
866}
867
868unsafe fn shell_save_html_doc(filename: *const c_char, doc: *mut _xmlDoc) -> c_int {
870 if filename.is_null() || doc.is_null() {
871 return -1;
872 }
873 let buf = io::buf_create(-1);
874 if buf.is_null() {
875 return -1;
876 }
877 let ret = crate::xml::html::doc_dump(buf, doc);
878 if ret < 0 {
879 io::buf_free(buf);
880 return -1;
881 }
882 let content = io::buf_content(buf);
883 let len = io::buf_length(buf);
884 let written = if !content.is_null() && len > 0 {
885 let f = libc::fopen(filename, b"w\0".as_ptr() as *const c_char);
886 if f.is_null() {
887 io::buf_free(buf);
888 return -1;
889 }
890 let n = libc::fwrite(content as *const c_void, 1, len as usize, f);
891 libc::fclose(f);
892 n as c_int
893 } else {
894 0
895 };
896 io::buf_free(buf);
897 written
898}
899
900#[no_mangle]
904pub unsafe extern "C" fn xmlShellSave(
905 ctxt: *mut _xmlShellCtxt,
906 filename: *mut c_char,
907 _node: *mut _xmlNode,
908 _node2: *mut _xmlNode,
909) -> c_int {
910 if ctxt.is_null() || unsafe { (*ctxt).doc }.is_null() {
911 return -1;
912 }
913 let mut filename = filename;
914 if filename.is_null() || *filename == 0 {
915 filename = unsafe { (*ctxt).filename };
916 }
917 if filename.is_null() {
918 return -1;
919 }
920 if libc::access(filename, libc::W_OK) != 0 {
922 unsafe {
923 shell_generic_error(b"Cannot save to \0".as_ptr() as *const c_char, b"", b"");
924 shell_generic_error(filename, b"", b"\n");
925 }
926 return -1;
927 }
928 let typ = unsafe { (*(*ctxt).doc).type_ };
929 unsafe {
930 match typ {
931 t if t == xmlElementType::XML_DOCUMENT_NODE as c_int => {
932 if xmlSaveFile(filename, (*ctxt).doc) < 0 {
933 shell_generic_error(
934 b"Failed to save to \0".as_ptr() as *const c_char,
935 b"",
936 b"",
937 );
938 shell_generic_error(filename, b"", b"\n");
939 }
940 }
941 t if t == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int => {
942 if shell_save_html_doc(filename, (*ctxt).doc) < 0 {
943 shell_generic_error(
944 b"Failed to save to \0".as_ptr() as *const c_char,
945 b"",
946 b"",
947 );
948 shell_generic_error(filename, b"", b"\n");
949 }
950 }
951 _ => {
952 shell_generic_error(
953 b"To save to subparts of a document use the 'write' command\n\0".as_ptr()
954 as *const c_char,
955 b"",
956 b"",
957 );
958 return -1;
959 }
960 }
961 }
962 0
963}
964
965unsafe extern "C" fn shell_valid_error(_ctx: *mut c_void, msg: *const c_char) {
972 unsafe {
973 if !msg.is_null() {
974 out_cstr(stderr, msg);
975 }
976 }
977}
978
979unsafe fn shell_parse_dtd(dtd: *const c_char) -> *mut _xmlDtd {
987 if dtd.is_null() {
988 return ptr::null_mut();
989 }
990 let path = match unsafe { core::ffi::CStr::from_ptr(dtd) }.to_str() {
991 Ok(p) => p,
992 Err(_) => return ptr::null_mut(),
993 };
994 let content = match std::fs::read(path) {
995 Ok(c) => c,
996 Err(_) => return ptr::null_mut(),
997 };
998 let lower: Vec<u8> = content.iter().map(|b| b.to_ascii_lowercase()).collect();
1000 let pos = match find_subslice(&lower, b"<!doctype") {
1001 Some(p) => p,
1002 None => {
1005 let base = path.rsplit(['/', '\\']).next().unwrap_or(path);
1006 let name_c = bytes_to_xmlstr(base.as_bytes());
1007 let dtd_node =
1008 crate::xml::dtd::new_dtd(ptr::null_mut(), name_c, ptr::null_mut(), ptr::null_mut());
1009 if !name_c.is_null() {
1010 xmlFreeImpl(name_c as *mut c_void);
1011 }
1012 return dtd_node;
1013 }
1014 };
1015 let mut i = pos + b"<!doctype".len();
1016 while i < content.len() && (content[i] as char).is_ascii_whitespace() {
1018 i += 1;
1019 }
1020 let name_start = i;
1022 while i < content.len()
1023 && !(content[i] as char).is_ascii_whitespace()
1024 && content[i] != b'>'
1025 && content[i] != b'['
1026 {
1027 i += 1;
1028 }
1029 if i == name_start {
1030 return ptr::null_mut();
1031 }
1032 let name = &content[name_start..i];
1033
1034 while i < content.len() && (content[i] as char).is_ascii_whitespace() {
1036 i += 1;
1037 }
1038 let mut public_id: Option<&[u8]> = None;
1039 let mut system_id: Option<&[u8]> = None;
1040 if i < content.len() && content[i..].to_ascii_lowercase().starts_with(b"public") {
1041 i += b"public".len();
1042 while i < content.len() && (content[i] as char).is_ascii_whitespace() {
1043 i += 1;
1044 }
1045 if i < content.len() && content[i] == b'"' {
1046 let s = i + 1;
1047 let e = content[s..].iter().position(|&c| c == b'"').map(|p| s + p);
1048 if let Some(e) = e {
1049 public_id = Some(&content[s..e]);
1050 }
1051 }
1052 while i < content.len() && (content[i] as char).is_ascii_whitespace() {
1053 i += 1;
1054 }
1055 if i < content.len() && content[i] == b'"' {
1056 let s = i + 1;
1057 let e = content[s..].iter().position(|&c| c == b'"').map(|p| s + p);
1058 if let Some(e) = e {
1059 system_id = Some(&content[s..e]);
1060 }
1061 }
1062 } else if i < content.len() && content[i..].to_ascii_lowercase().starts_with(b"system") {
1063 i += b"system".len();
1064 while i < content.len() && (content[i] as char).is_ascii_whitespace() {
1065 i += 1;
1066 }
1067 if i < content.len() && content[i] == b'"' {
1068 let s = i + 1;
1069 let e = content[s..].iter().position(|&c| c == b'"').map(|p| s + p);
1070 if let Some(e) = e {
1071 system_id = Some(&content[s..e]);
1072 }
1073 }
1074 }
1075
1076 let name_c = bytes_to_xmlstr(name);
1077 let ext_c = match public_id {
1078 Some(v) => bytes_to_xmlstr(v),
1079 None => ptr::null_mut(),
1080 };
1081 let sys_c = match system_id {
1082 Some(v) => bytes_to_xmlstr(v),
1083 None => ptr::null_mut(),
1084 };
1085 let dtd_node = crate::xml::dtd::new_dtd(ptr::null_mut(), name_c, ext_c, sys_c);
1086 if !name_c.is_null() {
1087 xmlFreeImpl(name_c as *mut c_void);
1088 }
1089 if !ext_c.is_null() {
1090 xmlFreeImpl(ext_c as *mut c_void);
1091 }
1092 if !sys_c.is_null() {
1093 xmlFreeImpl(sys_c as *mut c_void);
1094 }
1095 dtd_node
1096}
1097
1098fn find_subslice(haystack: &[u8], needle: &[u8]) -> Option<usize> {
1100 if needle.is_empty() || haystack.len() < needle.len() {
1101 return None;
1102 }
1103 haystack.windows(needle.len()).position(|w| w == needle)
1104}
1105
1106unsafe fn bytes_to_xmlstr(bytes: &[u8]) -> *mut xmlChar {
1108 let buf = xmlMallocImpl(bytes.len() + 1) as *mut xmlChar;
1109 if buf.is_null() {
1110 return ptr::null_mut();
1111 }
1112 unsafe {
1113 ptr::copy_nonoverlapping(bytes.as_ptr(), buf, bytes.len());
1114 *buf.add(bytes.len()) = 0;
1115 }
1116 buf
1117}
1118
1119#[no_mangle]
1122pub unsafe extern "C" fn xmlShellValidate(
1123 ctxt: *mut _xmlShellCtxt,
1124 dtd: *mut c_char,
1125 _node: *mut _xmlNode,
1126 _node2: *mut _xmlNode,
1127) -> c_int {
1128 if ctxt.is_null() || unsafe { (*ctxt).doc }.is_null() {
1129 return -1;
1130 }
1131 let vctxt = xmlMallocZero(size_of::<_xmlValidCtxt>()) as *mut _xmlValidCtxt;
1133 if vctxt.is_null() {
1134 return -1;
1135 }
1136 unsafe {
1137 (*vctxt).error = Some(shell_valid_error);
1138 (*vctxt).warning = Some(shell_valid_error);
1139 }
1140 let mut res = -1;
1141 unsafe {
1142 if dtd.is_null() || *dtd == 0 {
1143 res = xmlValidateDocument(vctxt, (*ctxt).doc);
1144 } else {
1145 let subset = shell_parse_dtd(dtd as *const c_char);
1146 if !subset.is_null() {
1147 res = xmlValidateDtd(vctxt, (*ctxt).doc, subset);
1148 crate::xml::dtd::free_dtd(subset);
1149 }
1150 }
1151 }
1152 unsafe {
1153 xmlFreeImpl(vctxt as *mut c_void);
1154 }
1155 res
1156}
1157
1158#[no_mangle]
1166pub unsafe extern "C" fn xmlShellDu(
1167 ctxt: *mut _xmlShellCtxt,
1168 _arg: *mut c_char,
1169 tree: *mut _xmlNode,
1170 _node2: *mut _xmlNode,
1171) -> c_int {
1172 if ctxt.is_null() {
1173 return -1;
1174 }
1175 if tree.is_null() {
1176 return -1;
1177 }
1178 let out = unsafe { (*ctxt).output };
1179 let mut indent: c_int = 0;
1180 let mut node: *mut _xmlNode = tree;
1181 unsafe {
1182 while !node.is_null() {
1183 let typ = (*node).type_;
1184 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
1185 || typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
1186 {
1187 out_bytes(out, b"/\n");
1188 } else if typ == xmlElementType::XML_ELEMENT_NODE as c_int {
1189 let mut line = Vec::new();
1190 for _ in 0..indent {
1191 line.extend_from_slice(b" ");
1192 }
1193 if !(*node).ns.is_null() && !(*(*node).ns).prefix.is_null() {
1194 push_cstr(&mut line, (*(*node).ns).prefix as *const c_char);
1195 line.push(b':');
1196 }
1197 push_cstr(&mut line, (*node).name as *const c_char);
1198 line.push(b'\n');
1199 out_bytes(out, &line);
1200 }
1201
1202 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
1206 || typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
1207 {
1208 node = (*(node as *mut _xmlDoc)).children;
1209 } else if !(*node).children.is_null()
1210 && typ != xmlElementType::XML_ENTITY_REF_NODE as c_int
1211 {
1212 node = (*node).children;
1213 indent += 1;
1214 } else if node != tree && !(*node).next.is_null() {
1215 node = (*node).next;
1216 } else if node != tree {
1217 while node != tree {
1218 if !(*node).parent.is_null() {
1219 node = (*node).parent;
1220 indent -= 1;
1221 }
1222 if node != tree && !(*node).next.is_null() {
1223 node = (*node).next;
1224 break;
1225 }
1226 if (*node).parent.is_null() {
1227 node = ptr::null_mut();
1228 break;
1229 }
1230 if node == tree {
1231 node = ptr::null_mut();
1232 break;
1233 }
1234 }
1235 if node == tree {
1236 node = ptr::null_mut();
1237 }
1238 } else {
1239 node = ptr::null_mut();
1240 }
1241 }
1242 }
1243 0
1244}
1245
1246#[no_mangle]
1250pub unsafe extern "C" fn xmlShellPwd(
1251 _ctxt: *mut _xmlShellCtxt,
1252 buffer: *mut c_char,
1253 node: *mut _xmlNode,
1254 _node2: *mut _xmlNode,
1255) -> c_int {
1256 if node.is_null() || buffer.is_null() {
1257 return -1;
1258 }
1259
1260 let path = unsafe { shell_get_node_path(node) };
1261 if path.is_null() {
1262 return -1;
1263 }
1264
1265 let plen = unsafe { tree::xml_strlen(path) } as usize;
1267 let n = plen.min(498);
1268 unsafe {
1269 ptr::copy_nonoverlapping(path as *const u8, buffer as *mut u8, n);
1270 *buffer.add(n) = 0;
1271 *buffer.add(499) = b'0' as c_char;
1272 }
1273 unsafe {
1274 xmlFreeImpl(path as *mut c_void);
1275 }
1276 0
1277}
1278
1279unsafe fn xmlShellSetBase(ctxt: *mut _xmlShellCtxt, arg: *mut c_char, node: *mut _xmlNode) {
1285 let _ = ctxt;
1286 if !node.is_null() {
1287 unsafe {
1288 xmlNodeSetBase(node, arg as *const xmlChar);
1289 }
1290 }
1291}
1292
1293unsafe fn xmlShellRegisterNamespace(ctxt: *mut _xmlShellCtxt, arg: *mut c_char) -> c_int {
1296 let ns_list_dup = unsafe { xmlStrdup(arg as *const xmlChar) };
1297 if ns_list_dup.is_null() {
1298 return -1;
1299 }
1300 let mut next: *mut xmlChar = ns_list_dup;
1301 loop {
1302 if unsafe { *next == 0 } {
1303 break;
1304 }
1305 let prefix = next;
1307 let eq = unsafe { xmlStrchr(next, b'=' as xmlChar) };
1308 if eq.is_null() {
1309 unsafe {
1310 out_cstr(
1311 (*ctxt).output,
1312 b"setns: prefix=[nsuri] required\n\0".as_ptr() as *const c_char,
1313 );
1314 }
1315 unsafe {
1316 xmlFreeImpl(ns_list_dup as *mut c_void);
1317 }
1318 return -1;
1319 }
1320 unsafe {
1322 *(eq as *mut xmlChar) = 0;
1323 }
1324 let href = unsafe { eq.add(1) };
1325 let space = unsafe { xmlStrchr(href, b' ' as xmlChar) };
1327 if !space.is_null() {
1328 unsafe {
1329 *(space as *mut xmlChar) = 0;
1330 }
1331 next = unsafe { space.add(1) as *mut xmlChar };
1332 } else {
1333 next = unsafe { href.add(tree::xml_strlen(href) as usize) as *mut xmlChar };
1334 }
1335
1336 if unsafe { xmlXPathRegisterNs((*ctxt).pctxt, prefix, href) } != 0 {
1338 unsafe {
1339 let mut msg = Vec::new();
1340 msg.extend_from_slice(b"Error: unable to register NS with prefix=\"");
1341 push_cstr(&mut msg, prefix as *const c_char);
1342 msg.extend_from_slice(b"\" and href=\"");
1343 push_cstr(&mut msg, href as *const c_char);
1344 msg.extend_from_slice(b"\"\n");
1345 out_bytes((*ctxt).output, &msg);
1346 }
1347 unsafe {
1348 xmlFreeImpl(ns_list_dup as *mut c_void);
1349 }
1350 return -1;
1351 }
1352 }
1353 unsafe {
1354 xmlFreeImpl(ns_list_dup as *mut c_void);
1355 }
1356 0
1357}
1358
1359unsafe fn xmlShellRegisterRootNamespaces(ctxt: *mut _xmlShellCtxt, root: *mut _xmlNode) -> c_int {
1362 if root.is_null()
1363 || unsafe { (*root).type_ != xmlElementType::XML_ELEMENT_NODE as c_int }
1364 || unsafe { (*root).nsDef.is_null() }
1365 || ctxt.is_null()
1366 || unsafe { (*ctxt).pctxt.is_null() }
1367 {
1368 return -1;
1369 }
1370 let mut ns = unsafe { (*root).nsDef };
1371 while !ns.is_null() {
1372 if unsafe { (*ns).prefix.is_null() } {
1373 unsafe {
1374 xmlXPathRegisterNs(
1375 (*ctxt).pctxt,
1376 b"defaultns\0".as_ptr() as *const xmlChar,
1377 (*ns).href,
1378 );
1379 }
1380 } else {
1381 unsafe {
1382 xmlXPathRegisterNs((*ctxt).pctxt, (*ns).prefix, (*ns).href);
1383 }
1384 }
1385 ns = unsafe { (*ns).next };
1386 }
1387 0
1388}
1389
1390unsafe fn xmlShellGrep(ctxt: *mut _xmlShellCtxt, arg: *mut c_char, node: *mut _xmlNode) {
1393 if ctxt.is_null() || node.is_null() || arg.is_null() {
1394 return;
1395 }
1396 let mut node = node;
1397 while !node.is_null() {
1398 unsafe {
1399 let typ = (*node).type_;
1400 if typ == xmlElementType::XML_COMMENT_NODE as c_int {
1401 if !xmlStrstr((*node).content, arg as *const xmlChar).is_null() {
1402 let path = shell_get_node_path(node);
1403 if !path.is_null() {
1404 let mut line = Vec::new();
1405 push_cstr(&mut line, path as *const c_char);
1406 line.extend_from_slice(b" : ");
1407 out_bytes((*ctxt).output, &line);
1408 xmlFreeImpl(path as *mut c_void);
1409 }
1410 xmlShellList(ctxt, ptr::null_mut(), node, ptr::null_mut());
1411 }
1412 } else if typ == xmlElementType::XML_TEXT_NODE as c_int {
1413 if !xmlStrstr((*node).content, arg as *const xmlChar).is_null() {
1414 let path = shell_get_node_path((*node).parent);
1415 if !path.is_null() {
1416 let mut line = Vec::new();
1417 push_cstr(&mut line, path as *const c_char);
1418 line.extend_from_slice(b" : ");
1419 out_bytes((*ctxt).output, &line);
1420 xmlFreeImpl(path as *mut c_void);
1421 }
1422 xmlShellList(ctxt, ptr::null_mut(), (*node).parent, ptr::null_mut());
1423 }
1424 }
1425
1426 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
1430 || typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
1431 {
1432 node = (*(node as *mut _xmlDoc)).children;
1433 } else if !(*node).children.is_null()
1434 && typ != xmlElementType::XML_ENTITY_REF_NODE as c_int
1435 {
1436 node = (*node).children;
1437 } else if !(*node).next.is_null() {
1438 node = (*node).next;
1439 } else {
1440 while !node.is_null() {
1441 if !(*node).parent.is_null() {
1442 node = (*node).parent;
1443 }
1444 if !(*node).next.is_null() {
1445 node = (*node).next;
1446 break;
1447 }
1448 if (*node).parent.is_null() {
1449 node = ptr::null_mut();
1450 break;
1451 }
1452 }
1453 }
1454 }
1455 }
1456}
1457
1458unsafe fn shell_result_type_error(arg: *const c_char, typ: c_int) {
1461 if typ == xmlXPathObjectType::XPATH_UNDEFINED as c_int {
1462 unsafe { shell_generic_error(arg, b": ", b"no such node\n") };
1463 } else if typ == xmlXPathObjectType::XPATH_BOOLEAN as c_int {
1464 unsafe { shell_generic_error(arg, b" is a Boolean", b"\n") };
1465 } else if typ == xmlXPathObjectType::XPATH_NUMBER as c_int {
1466 unsafe { shell_generic_error(arg, b" is a number", b"\n") };
1467 } else if typ == xmlXPathObjectType::XPATH_STRING as c_int {
1468 unsafe { shell_generic_error(arg, b" is a string", b"\n") };
1469 } else if typ == xmlXPathObjectType::XPATH_POINT as c_int {
1470 unsafe { shell_generic_error(arg, b" is a point", b"\n") };
1471 } else if typ == xmlXPathObjectType::XPATH_RANGE as c_int {
1472 unsafe { shell_generic_error(arg, b" is a range", b"\n") };
1473 } else if typ == xmlXPathObjectType::XPATH_LOCATIONSET as c_int {
1474 unsafe { shell_generic_error(arg, b" is a range", b"\n") };
1475 } else if typ == xmlXPathObjectType::XPATH_USERS as c_int {
1476 unsafe { shell_generic_error(arg, b" is user-defined", b"\n") };
1477 } else if typ == xmlXPathObjectType::XPATH_XSLT_TREE as c_int {
1478 unsafe { shell_generic_error(arg, b" is an XSLT value tree", b"\n") };
1479 }
1480}
1481
1482unsafe fn shell_build_prompt(ctxt: *mut _xmlShellCtxt) -> Vec<u8> {
1489 let mut p = Vec::new();
1490 let node = unsafe { (*ctxt).node };
1491 let doc = unsafe { (*ctxt).doc };
1492 if node == doc as *mut _xmlNode {
1493 p.extend_from_slice(b"/ > ");
1494 } else if !node.is_null() && !unsafe { (*node).name }.is_null() {
1495 unsafe {
1496 if !(*node).ns.is_null() && !(*(*node).ns).prefix.is_null() {
1497 push_cstr(&mut p, (*(*node).ns).prefix as *const c_char);
1498 p.push(b':');
1499 }
1500 push_cstr(&mut p, (*node).name as *const c_char);
1501 }
1502 p.extend_from_slice(b" > ");
1503 } else {
1504 p.extend_from_slice(b"? > ");
1505 }
1506 p.push(0);
1507 p
1508}
1509
1510unsafe fn shell_print_help(ctxt: *mut _xmlShellCtxt) {
1512 let out = unsafe { (*ctxt).output };
1513 const HELP: &[&[u8]] = &[
1514 b"\tbase display XML base of the node\n",
1515 b"\tsetbase URI change the XML base of the node\n",
1516 b"\tbye leave shell\n",
1517 b"\tcat [node] display node or current node\n",
1518 b"\tcd [path] change directory to path or to root\n",
1519 b"\tdir [path] dumps information about the node (namespace, attributes, content)\n",
1520 b"\tdu [path] show the structure of the subtree under path or the current node\n",
1521 b"\texit leave shell\n",
1522 b"\thelp display this help\n",
1523 b"\tfree display memory usage\n",
1524 b"\tload [name] load a new document with name\n",
1525 b"\tls [path] list contents of path or the current directory\n",
1526 b"\txpath expr evaluate the XPath expression in that context and print the result\n",
1527 b"\tsetns nsreg register a namespace to a prefix in the XPath evaluation context\n",
1528 b"\t format for nsreg is: prefix=[nsuri] (i.e. prefix= unsets a prefix)\n",
1529 b"\tsetrootns register all namespace found on the root element\n",
1530 b"\t the default namespace if any uses 'defaultns' prefix\n",
1531 b"\tpwd display current working directory\n",
1532 b"\twhereis display absolute path of [path] or current working directory\n",
1533 b"\tquit leave shell\n",
1534 b"\tsave [name] save this document to name or the original name\n",
1535 b"\twrite [name] write the current node to the filename\n",
1536 b"\tvalidate check the document for errors\n",
1537 b"\tgrep string search for a string in the subtree\n",
1538 ];
1539 for line in HELP {
1540 unsafe {
1541 out_bytes(out, line);
1542 }
1543 }
1544}
1545
1546#[no_mangle]
1550pub unsafe extern "C" fn xmlShell(
1551 doc: *mut _xmlDoc,
1552 filename: *mut c_char,
1553 input: xmlShellReadlineFunc,
1554 output: *mut c_void,
1555) {
1556 if doc.is_null() || filename.is_null() || input.is_none() {
1557 return;
1558 }
1559 let output = if output.is_null() {
1560 unsafe { stdout }
1561 } else {
1562 output
1563 };
1564
1565 let ctxt = xmlMallocZero(size_of::<_xmlShellCtxt>()) as *mut _xmlShellCtxt;
1566 if ctxt.is_null() {
1567 return;
1568 }
1569 unsafe {
1570 (*ctxt).loaded = 0;
1571 (*ctxt).doc = doc;
1572 (*ctxt).input = input;
1573 (*ctxt).output = output;
1574 (*ctxt).filename = xmlStrdup(filename as *const xmlChar) as *mut c_char;
1575 (*ctxt).node = doc as *mut _xmlNode;
1576 (*ctxt).pctxt = xmlXPathNewContext(doc);
1577 }
1578 if unsafe { (*ctxt).pctxt }.is_null() {
1579 unsafe {
1580 xmlFreeImpl(ctxt as *mut c_void);
1581 }
1582 return;
1583 }
1584
1585 let mut cmdline: *mut c_char = ptr::null_mut();
1586 loop {
1587 let prompt = unsafe { shell_build_prompt(ctxt) };
1589 let readline = unsafe { (*ctxt).input };
1590 cmdline = match readline {
1591 Some(f) => f(prompt.as_ptr() as *mut c_char),
1592 None => break,
1593 };
1594 if cmdline.is_null() {
1595 break;
1596 }
1597
1598 let clen = unsafe { libc::strlen(cmdline) } as usize;
1600 let cbytes = unsafe { core::slice::from_raw_parts(cmdline as *const u8, clen) };
1601 let mut i = 0usize;
1602 while i < clen && (cbytes[i] == b' ' || cbytes[i] == b'\t') {
1603 i += 1;
1604 }
1605 let mut command: Vec<u8> = Vec::new();
1606 while i < clen
1607 && cbytes[i] != b' '
1608 && cbytes[i] != b'\t'
1609 && cbytes[i] != b'\n'
1610 && cbytes[i] != b'\r'
1611 {
1612 command.push(cbytes[i]);
1613 i += 1;
1614 }
1615 if command.is_empty() {
1616 unsafe {
1617 libc::free(cmdline as *mut c_void);
1618 }
1619 cmdline = ptr::null_mut();
1620 continue;
1621 }
1622
1623 while i < clen && (cbytes[i] == b' ' || cbytes[i] == b'\t') {
1625 i += 1;
1626 }
1627 let mut arg: Vec<u8> = Vec::new();
1628 while i < clen && cbytes[i] != b'\n' && cbytes[i] != b'\r' {
1629 arg.push(cbytes[i]);
1630 i += 1;
1631 }
1632
1633 command.push(0);
1635 let cmd: &[u8] = &command;
1636 let mut argn = arg.clone();
1637 argn.push(0);
1638 let arg_cstr: *mut c_char = argn.as_mut_ptr() as *mut c_char;
1639 let arg_xml: *const xmlChar = argn.as_ptr() as *const xmlChar;
1640
1641 if cmd == b"exit\0" || cmd == b"quit\0" || cmd == b"bye\0" {
1643 break;
1644 }
1645 if cmd == b"help\0" {
1646 unsafe { shell_print_help(ctxt) };
1647 } else if cmd == b"validate\0" {
1648 unsafe {
1649 xmlShellValidate(ctxt, arg_cstr, ptr::null_mut(), ptr::null_mut());
1650 }
1651 } else if cmd == b"load\0" {
1652 unsafe {
1653 xmlShellLoad(ctxt, arg_cstr, ptr::null_mut(), ptr::null_mut());
1654 }
1655 } else if cmd == b"save\0" {
1656 unsafe {
1657 xmlShellSave(ctxt, arg_cstr, ptr::null_mut(), ptr::null_mut());
1658 }
1659 } else if cmd == b"write\0" {
1660 if arg.is_empty() {
1661 unsafe {
1662 shell_generic_error(
1663 b"Write command requires a filename argument\n\0".as_ptr() as *const c_char,
1664 b"",
1665 b"",
1666 );
1667 }
1668 } else {
1669 unsafe {
1670 xmlShellWrite(ctxt, arg_cstr, (*ctxt).node, ptr::null_mut());
1671 }
1672 }
1673 } else if cmd == b"grep\0" {
1674 unsafe {
1675 xmlShellGrep(ctxt, arg_cstr, (*ctxt).node);
1676 }
1677 } else if cmd == b"free\0" {
1678 unsafe {
1679 if arg.is_empty() {
1680 crate::abi::allocator::xmlMemShow((*ctxt).output, 0);
1681 } else {
1682 let mut len: c_int = 0;
1683 let arg_s = core::str::from_utf8(&argn[..argn.len() - 1]).unwrap_or("");
1684 if let Ok(v) = arg_s.trim().parse::<c_int>() {
1685 len = v;
1686 }
1687 crate::abi::allocator::xmlMemShow((*ctxt).output, len);
1688 }
1689 }
1690 } else if cmd == b"pwd\0" {
1691 let mut dir = [0 as c_char; 500];
1692 unsafe {
1693 if xmlShellPwd(ctxt, dir.as_mut_ptr(), (*ctxt).node, ptr::null_mut()) == 0 {
1694 let mut line = Vec::new();
1695 push_cstr(&mut line, dir.as_mut_ptr());
1696 line.extend_from_slice(b"\n");
1697 out_bytes((*ctxt).output, &line);
1698 }
1699 }
1700 } else if cmd == b"du\0" {
1701 unsafe {
1702 if arg.is_empty() {
1703 xmlShellDu(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
1704 } else {
1705 (*(*ctxt).pctxt).node = (*ctxt).node;
1706 let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
1707 if !list.is_null() {
1708 let typ = (*list).type_;
1709 if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
1710 let ns = (*list).nodesetval as *mut _xmlNodeSet;
1711 if !ns.is_null() {
1712 for indx in 0..(*ns).nodeNr {
1713 let n = *(*ns).nodeTab.add(indx as usize);
1714 xmlShellDu(ctxt, ptr::null_mut(), n, ptr::null_mut());
1715 }
1716 }
1717 } else {
1718 shell_result_type_error(arg_cstr, typ);
1719 }
1720 xmlXPathFreeObject(list);
1721 } else {
1722 shell_generic_error(arg_cstr, b": ", b"no such node\n");
1723 }
1724 (*(*ctxt).pctxt).node = ptr::null_mut();
1725 }
1726 }
1727 } else if cmd == b"base\0" {
1728 unsafe {
1729 xmlShellBase(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
1730 }
1731 } else if cmd == b"setns\0" {
1732 unsafe {
1733 if arg.is_empty() {
1734 shell_generic_error(
1735 b"setns: prefix=[nsuri] required\n\0".as_ptr() as *const c_char,
1736 b"",
1737 b"",
1738 );
1739 } else {
1740 xmlShellRegisterNamespace(ctxt, arg_cstr);
1741 }
1742 }
1743 } else if cmd == b"setrootns\0" {
1744 unsafe {
1745 let root = tree::doc_get_root_element((*ctxt).doc);
1746 xmlShellRegisterRootNamespaces(ctxt, root);
1747 }
1748 } else if cmd == b"xpath\0" {
1749 unsafe {
1750 if arg.is_empty() {
1751 shell_generic_error(
1752 b"xpath: expression required\n\0".as_ptr() as *const c_char,
1753 b"",
1754 b"",
1755 );
1756 } else {
1757 (*(*ctxt).pctxt).node = (*ctxt).node;
1758 let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
1759 xmlXPathDebugDumpObject((*ctxt).output, list, 0);
1760 xmlXPathFreeObject(list);
1761 }
1762 }
1763 } else if cmd == b"setbase\0" {
1764 unsafe {
1765 xmlShellSetBase(ctxt, arg_cstr, (*ctxt).node);
1766 }
1767 } else if cmd == b"ls\0" || cmd == b"dir\0" {
1768 let is_dir = cmd == b"dir\0";
1769 unsafe {
1770 if arg.is_empty() {
1771 if is_dir {
1772 xmlShellDir(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
1773 } else {
1774 xmlShellList(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
1775 }
1776 } else {
1777 (*(*ctxt).pctxt).node = (*ctxt).node;
1778 let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
1779 if !list.is_null() {
1780 let typ = (*list).type_;
1781 if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
1782 let ns = (*list).nodesetval as *mut _xmlNodeSet;
1783 if !ns.is_null() {
1784 for indx in 0..(*ns).nodeNr {
1785 let n = *(*ns).nodeTab.add(indx as usize);
1786 if is_dir {
1787 xmlShellDir(ctxt, ptr::null_mut(), n, ptr::null_mut());
1788 } else {
1789 xmlShellList(ctxt, ptr::null_mut(), n, ptr::null_mut());
1790 }
1791 }
1792 }
1793 } else {
1794 shell_result_type_error(arg_cstr, typ);
1795 }
1796 xmlXPathFreeObject(list);
1797 } else {
1798 shell_generic_error(arg_cstr, b": ", b"no such node\n");
1799 }
1800 (*(*ctxt).pctxt).node = ptr::null_mut();
1801 }
1802 }
1803 } else if cmd == b"whereis\0" {
1804 let mut dir = [0 as c_char; 500];
1805 unsafe {
1806 if arg.is_empty() {
1807 if xmlShellPwd(ctxt, dir.as_mut_ptr(), (*ctxt).node, ptr::null_mut()) == 0 {
1808 let mut line = Vec::new();
1809 push_cstr(&mut line, dir.as_mut_ptr());
1810 line.extend_from_slice(b"\n");
1811 out_bytes((*ctxt).output, &line);
1812 }
1813 } else {
1814 (*(*ctxt).pctxt).node = (*ctxt).node;
1815 let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
1816 if !list.is_null() {
1817 let typ = (*list).type_;
1818 if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
1819 let ns = (*list).nodesetval as *mut _xmlNodeSet;
1820 if !ns.is_null() {
1821 for indx in 0..(*ns).nodeNr {
1822 let n = *(*ns).nodeTab.add(indx as usize);
1823 if xmlShellPwd(ctxt, dir.as_mut_ptr(), n, ptr::null_mut()) == 0
1824 {
1825 let mut line = Vec::new();
1826 push_cstr(&mut line, dir.as_mut_ptr());
1827 line.extend_from_slice(b"\n");
1828 out_bytes((*ctxt).output, &line);
1829 }
1830 }
1831 }
1832 } else {
1833 shell_result_type_error(arg_cstr, typ);
1834 }
1835 xmlXPathFreeObject(list);
1836 } else {
1837 shell_generic_error(arg_cstr, b": ", b"no such node\n");
1838 }
1839 (*(*ctxt).pctxt).node = ptr::null_mut();
1840 }
1841 }
1842 } else if cmd == b"cd\0" {
1843 unsafe {
1844 if arg.is_empty() {
1845 (*ctxt).node = (*ctxt).doc as *mut _xmlNode;
1846 } else {
1847 let mut argn = argn;
1849 let l = argn.len();
1850 if l >= 3 && argn[l - 2] == b'/' {
1851 argn[l - 2] = 0;
1852 }
1853 (*(*ctxt).pctxt).node = (*ctxt).node;
1854 let list = xmlXPathEval(argn.as_ptr() as *const xmlChar, (*ctxt).pctxt);
1855 if !list.is_null() {
1856 let typ = (*list).type_;
1857 if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
1858 let ns = (*list).nodesetval as *mut _xmlNodeSet;
1859 if !ns.is_null() {
1860 if (*ns).nodeNr == 1 {
1861 (*ctxt).node = *(*ns).nodeTab;
1862 if !(*ctxt).node.is_null()
1863 && (*(*ctxt).node).type_
1864 == xmlElementType::XML_NAMESPACE_DECL as c_int
1865 {
1866 shell_generic_error(
1867 b"cannot cd to namespace\n\0".as_ptr() as *const c_char,
1868 b"",
1869 b"",
1870 );
1871 (*ctxt).node = ptr::null_mut();
1872 }
1873 } else {
1874 let mut msg = Vec::new();
1875 push_cstr(&mut msg, arg_cstr);
1876 msg.extend_from_slice(b" is a ");
1877 msg.extend_from_slice((*ns).nodeNr.to_string().as_bytes());
1878 msg.extend_from_slice(b" Node Set\n");
1879 out_bytes(stderr, &msg);
1880 }
1881 } else {
1882 let mut msg = Vec::new();
1883 push_cstr(&mut msg, arg_cstr);
1884 msg.extend_from_slice(b" is an empty Node Set\n");
1885 out_bytes(stderr, &msg);
1886 }
1887 } else {
1888 shell_result_type_error(arg_cstr, typ);
1889 }
1890 xmlXPathFreeObject(list);
1891 } else {
1892 shell_generic_error(arg_cstr, b": ", b"no such node\n");
1893 }
1894 (*(*ctxt).pctxt).node = ptr::null_mut();
1895 }
1896 }
1897 } else if cmd == b"cat\0" {
1898 unsafe {
1899 if arg.is_empty() {
1900 xmlShellCat(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
1901 } else {
1902 (*(*ctxt).pctxt).node = (*ctxt).node;
1907 let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
1908 if !list.is_null() {
1909 let typ = (*list).type_;
1910 if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
1911 let ns = (*list).nodesetval as *mut _xmlNodeSet;
1912 if !ns.is_null() {
1913 for indx in 0..(*ns).nodeNr {
1914 if i > 0 {
1915 out_bytes((*ctxt).output, b" -------\n");
1916 }
1917 let n = *(*ns).nodeTab.add(indx as usize);
1918 xmlShellCat(ctxt, ptr::null_mut(), n, ptr::null_mut());
1919 }
1920 }
1921 } else {
1922 shell_result_type_error(arg_cstr, typ);
1923 }
1924 xmlXPathFreeObject(list);
1925 } else {
1926 shell_generic_error(arg_cstr, b": ", b"no such node\n");
1927 }
1928 (*(*ctxt).pctxt).node = ptr::null_mut();
1929 }
1930 }
1931 } else {
1932 let mut msg = Vec::new();
1933 msg.extend_from_slice(b"Unknown command ");
1934 msg.extend_from_slice(&command[..command.len() - 1]);
1935 msg.extend_from_slice(b"\n");
1936 unsafe {
1937 out_bytes(stderr, &msg);
1938 }
1939 }
1940
1941 unsafe {
1942 libc::free(cmdline as *mut c_void);
1943 }
1944 cmdline = ptr::null_mut();
1945 }
1946
1947 unsafe {
1949 xmlXPathFreeContext((*ctxt).pctxt);
1950 if (*ctxt).loaded != 0 {
1951 tree::free_doc((*ctxt).doc);
1952 }
1953 if !(*ctxt).filename.is_null() {
1954 xmlFreeImpl((*ctxt).filename as *mut c_void);
1955 }
1956 xmlFreeImpl(ctxt as *mut c_void);
1957 if !cmdline.is_null() {
1958 libc::free(cmdline as *mut c_void);
1959 }
1960 }
1961}