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]
515pub unsafe extern "C" fn xmlShellPrintXPathError(errorType: c_int, arg: *const c_char) {
516 let default_arg = b"Result\0";
517 let arg = if arg.is_null() {
518 default_arg.as_ptr() as *const c_char
519 } else {
520 arg
521 };
522
523 if errorType == xmlXPathObjectType::XPATH_UNDEFINED as c_int {
524 unsafe { shell_generic_error(arg, b": ", b"no such node\n") };
525 } else if errorType == xmlXPathObjectType::XPATH_BOOLEAN as c_int {
526 unsafe { shell_generic_error(arg, b" is a Boolean", b"\n") };
527 } else if errorType == xmlXPathObjectType::XPATH_NUMBER as c_int {
528 unsafe { shell_generic_error(arg, b" is a number", b"\n") };
529 } else if errorType == xmlXPathObjectType::XPATH_STRING as c_int {
530 unsafe { shell_generic_error(arg, b" is a string", b"\n") };
531 } else if errorType == xmlXPathObjectType::XPATH_POINT as c_int {
532 unsafe { shell_generic_error(arg, b" is a point", b"\n") };
533 } else if errorType == xmlXPathObjectType::XPATH_RANGE as c_int
534 || errorType == xmlXPathObjectType::XPATH_LOCATIONSET as c_int
535 {
536 unsafe { shell_generic_error(arg, b" is a range", b"\n") };
537 } else if errorType == xmlXPathObjectType::XPATH_USERS as c_int {
538 unsafe { shell_generic_error(arg, b" is user-defined", b"\n") };
539 } else if errorType == xmlXPathObjectType::XPATH_XSLT_TREE as c_int {
540 unsafe { shell_generic_error(arg, b" is an XSLT value tree", b"\n") };
541 }
542}
543
544unsafe fn xmlShellPrintNodeCtxt(ctxt: *mut _xmlShellCtxt, node: *mut _xmlNode) {
547 if node.is_null() {
548 return;
549 }
550 let fp = if ctxt.is_null() {
551 unsafe { stdout }
552 } else {
553 (*ctxt).output
554 };
555
556 let typ = unsafe { (*node).type_ };
557 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int {
558 tree::xmlDocDump(fp, node as *mut _xmlDoc);
559 } else if typ == xmlElementType::XML_ATTRIBUTE_NODE as c_int {
560 unsafe {
561 debug::xmlDebugDumpAttrList(fp as *mut debug::_IO_FILE, node as *mut _xmlAttr, 0);
562 }
563 } else {
564 unsafe {
565 shell_elem_dump(fp, (*node).doc, node);
566 }
567 }
568 unsafe {
569 out_bytes(fp, b"\n");
570 }
571}
572
573#[no_mangle]
590pub unsafe extern "C" fn xmlShellPrintNode(node: *mut _xmlNode) {
591 unsafe { xmlShellPrintNodeCtxt(ptr::null_mut(), node) };
592}
593
594#[no_mangle]
614pub unsafe extern "C" fn xmlShellPrintXPathResult(list: *mut _xmlXPathObject) {
615 unsafe {
616 xmlXPathDebugDumpObject(stdout, list, 0);
617 }
618}
619
620#[no_mangle]
642pub unsafe extern "C" fn xmlShellList(
643 ctxt: *mut _xmlShellCtxt,
644 _arg: *mut c_char,
645 node: *mut _xmlNode,
646 _node2: *mut _xmlNode,
647) -> c_int {
648 if ctxt.is_null() {
649 return 0;
650 }
651 if node.is_null() {
652 unsafe {
653 out_bytes((*ctxt).output, b"NULL\n");
654 }
655 return 0;
656 }
657 let typ = unsafe { (*node).type_ };
658 let mut cur: *mut _xmlNode;
659 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
660 || typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
661 {
662 cur = unsafe { (*(node as *mut _xmlDoc)).children };
663 } else if typ == xmlElementType::XML_NAMESPACE_DECL as c_int {
664 unsafe {
665 debug::xmlLsOneNode((*ctxt).output as *mut debug::_IO_FILE, node);
666 }
667 return 0;
668 } else if !unsafe { (*node).children }.is_null() {
669 cur = unsafe { (*node).children };
670 } else {
671 unsafe {
672 debug::xmlLsOneNode((*ctxt).output as *mut debug::_IO_FILE, node);
673 }
674 return 0;
675 }
676 while !cur.is_null() {
677 unsafe {
678 debug::xmlLsOneNode((*ctxt).output as *mut debug::_IO_FILE, cur);
679 cur = (*cur).next;
680 }
681 }
682 0
683}
684
685#[no_mangle]
703pub unsafe extern "C" fn xmlShellBase(
704 ctxt: *mut _xmlShellCtxt,
705 _arg: *mut c_char,
706 node: *mut _xmlNode,
707 _node2: *mut _xmlNode,
708) -> c_int {
709 if ctxt.is_null() {
710 return 0;
711 }
712 if node.is_null() {
713 unsafe {
714 out_bytes((*ctxt).output, b"NULL\n");
715 }
716 return 0;
717 }
718
719 let base = unsafe { xmlNodeGetBase((*node).doc, node) };
720
721 if base.is_null() {
722 unsafe {
723 out_bytes((*ctxt).output, b" No base found !!!\n");
724 }
725 } else {
726 unsafe {
727 out_cstr((*ctxt).output, base as *const c_char);
728 out_bytes((*ctxt).output, b"\n");
729 xmlFreeImpl(base as *mut c_void);
730 }
731 }
732 0
733}
734
735#[no_mangle]
753pub unsafe extern "C" fn xmlShellDir(
754 ctxt: *mut _xmlShellCtxt,
755 _arg: *mut c_char,
756 node: *mut _xmlNode,
757 _node2: *mut _xmlNode,
758) -> c_int {
759 if ctxt.is_null() {
760 return 0;
761 }
762 if node.is_null() {
763 unsafe {
764 out_bytes((*ctxt).output, b"NULL\n");
765 }
766 return 0;
767 }
768 let typ = unsafe { (*node).type_ };
769 unsafe {
770 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
771 || typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
772 {
773 debug::xmlDebugDumpDocumentHead(
774 (*ctxt).output as *mut debug::_IO_FILE,
775 node as *mut _xmlDoc,
776 );
777 } else if typ == xmlElementType::XML_ATTRIBUTE_NODE as c_int {
778 debug::xmlDebugDumpAttr(
779 (*ctxt).output as *mut debug::_IO_FILE,
780 node as *mut _xmlAttr,
781 0,
782 );
783 } else {
784 debug::xmlDebugDumpOneNode((*ctxt).output as *mut debug::_IO_FILE, node, 0);
785 }
786 }
787 0
788}
789
790#[no_mangle]
813pub unsafe extern "C" fn xmlShellCat(
814 ctxt: *mut _xmlShellCtxt,
815 _arg: *mut c_char,
816 node: *mut _xmlNode,
817 _node2: *mut _xmlNode,
818) -> c_int {
819 if ctxt.is_null() {
820 return 0;
821 }
822 if node.is_null() {
823 unsafe {
824 out_bytes((*ctxt).output, b"NULL\n");
825 }
826 return 0;
827 }
828 let out = unsafe { (*ctxt).output };
829 let is_html =
830 unsafe { (*(*ctxt).doc).type_ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int };
831 let typ = unsafe { (*node).type_ };
832 unsafe {
833 if is_html {
834 if typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int {
835 shell_html_doc_dump(out, node as *mut _xmlDoc);
836 } else {
837 shell_html_node_dump_file(out, node);
838 }
839 } else if typ == xmlElementType::XML_DOCUMENT_NODE as c_int {
840 tree::xmlDocDump(out, node as *mut _xmlDoc);
841 } else {
842 shell_elem_dump(out, (*ctxt).doc, node);
843 }
844 out_bytes(out, b"\n");
845 }
846 0
847}
848
849#[no_mangle]
867pub unsafe extern "C" fn xmlShellLoad(
868 ctxt: *mut _xmlShellCtxt,
869 filename: *mut c_char,
870 _node: *mut _xmlNode,
871 _node2: *mut _xmlNode,
872) -> c_int {
873 if ctxt.is_null() || filename.is_null() {
874 return -1;
875 }
876 let mut html = 0;
877 if !unsafe { (*ctxt).doc }.is_null() {
878 html = unsafe {
879 ((*(*ctxt).doc).type_ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int) as c_int
880 };
881 }
882
883 let doc: *mut _xmlDoc = if html != 0 {
884 unsafe { crate::xml::html::parse_file(filename, ptr::null()) }
887 } else {
888 unsafe { xmlReadFile(filename, ptr::null(), 0) }
889 };
890
891 if !doc.is_null() {
892 unsafe {
893 if (*ctxt).loaded == 1 {
894 tree::free_doc((*ctxt).doc);
895 }
896 (*ctxt).loaded = 1;
897 xmlXPathFreeContext((*ctxt).pctxt);
898 if !(*ctxt).filename.is_null() {
899 xmlFreeImpl((*ctxt).filename as *mut c_void);
900 }
901 (*ctxt).doc = doc;
902 (*ctxt).node = doc as *mut _xmlNode;
903 (*ctxt).pctxt = xmlXPathNewContext(doc);
904 (*ctxt).filename = xmlCanonicPath(filename) as *mut c_char;
905 }
906 0
907 } else {
908 -1
909 }
910}
911
912#[no_mangle]
931pub unsafe extern "C" fn xmlShellWrite(
932 ctxt: *mut _xmlShellCtxt,
933 filename: *mut c_char,
934 node: *mut _xmlNode,
935 _node2: *mut _xmlNode,
936) -> c_int {
937 if node.is_null() {
938 return -1;
939 }
940 if filename.is_null() || *filename == 0 {
941 unsafe {
942 shell_generic_error(
943 c"Write command requires a filename argument\n".as_ptr() as *const c_char,
944 b"",
945 b"",
946 );
947 }
948 return -1;
949 }
950 if libc::access(filename, libc::W_OK) != 0 {
952 unsafe {
953 shell_generic_error(c"Cannot write to ".as_ptr() as *const c_char, b"", b"");
954 shell_generic_error(filename, b"", b"\n");
955 }
956 return -1;
957 }
958 let typ = unsafe { (*node).type_ };
959 unsafe {
960 match typ {
961 t if t == xmlElementType::XML_DOCUMENT_NODE as c_int => {
962 if xmlSaveFile(filename, (*ctxt).doc) < -1 {
963 shell_generic_error(c"Failed to write to ".as_ptr() as *const c_char, b"", b"");
964 shell_generic_error(filename, b"", b"\n");
965 return -1;
966 }
967 }
968 t if t == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int => {
969 if shell_save_html_doc(filename, (*ctxt).doc) < 0 {
971 shell_generic_error(c"Failed to write to ".as_ptr() as *const c_char, b"", b"");
972 shell_generic_error(filename, b"", b"\n");
973 return -1;
974 }
975 }
976 _ => {
977 let f = libc::fopen(filename, c"w".as_ptr() as *const c_char);
978 if f.is_null() {
979 shell_generic_error(c"Failed to write to ".as_ptr() as *const c_char, b"", b"");
980 shell_generic_error(filename, b"", b"\n");
981 return -1;
982 }
983 shell_elem_dump(f as *mut c_void, (*ctxt).doc, node);
984 libc::fclose(f);
985 }
986 }
987 }
988 0
989}
990
991unsafe fn shell_save_html_doc(filename: *const c_char, doc: *mut _xmlDoc) -> c_int {
993 if filename.is_null() || doc.is_null() {
994 return -1;
995 }
996 let buf = io::buf_create(-1);
997 if buf.is_null() {
998 return -1;
999 }
1000 let ret = crate::xml::html::doc_dump(buf, doc);
1001 if ret < 0 {
1002 io::buf_free(buf);
1003 return -1;
1004 }
1005 let content = io::buf_content(buf);
1006 let len = io::buf_length(buf);
1007 let written = if !content.is_null() && len > 0 {
1008 let f = libc::fopen(filename, c"w".as_ptr() as *const c_char);
1009 if f.is_null() {
1010 io::buf_free(buf);
1011 return -1;
1012 }
1013 let n = libc::fwrite(content as *const c_void, 1, len as usize, f);
1014 libc::fclose(f);
1015 n as c_int
1016 } else {
1017 0
1018 };
1019 io::buf_free(buf);
1020 written
1021}
1022
1023#[no_mangle]
1042pub unsafe extern "C" fn xmlShellSave(
1043 ctxt: *mut _xmlShellCtxt,
1044 filename: *mut c_char,
1045 _node: *mut _xmlNode,
1046 _node2: *mut _xmlNode,
1047) -> c_int {
1048 if ctxt.is_null() || unsafe { (*ctxt).doc }.is_null() {
1049 return -1;
1050 }
1051 let mut filename = filename;
1052 if filename.is_null() || *filename == 0 {
1053 filename = unsafe { (*ctxt).filename };
1054 }
1055 if filename.is_null() {
1056 return -1;
1057 }
1058 if libc::access(filename, libc::W_OK) != 0 {
1060 unsafe {
1061 shell_generic_error(c"Cannot save to ".as_ptr() as *const c_char, b"", b"");
1062 shell_generic_error(filename, b"", b"\n");
1063 }
1064 return -1;
1065 }
1066 let typ = unsafe { (*(*ctxt).doc).type_ };
1067 unsafe {
1068 match typ {
1069 t if t == xmlElementType::XML_DOCUMENT_NODE as c_int => {
1070 if xmlSaveFile(filename, (*ctxt).doc) < 0 {
1071 shell_generic_error(c"Failed to save to ".as_ptr() as *const c_char, b"", b"");
1072 shell_generic_error(filename, b"", b"\n");
1073 }
1074 }
1075 t if t == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int => {
1076 if shell_save_html_doc(filename, (*ctxt).doc) < 0 {
1077 shell_generic_error(c"Failed to save to ".as_ptr() as *const c_char, b"", b"");
1078 shell_generic_error(filename, b"", b"\n");
1079 }
1080 }
1081 _ => {
1082 shell_generic_error(
1083 c"To save to subparts of a document use the 'write' command\n".as_ptr()
1084 as *const c_char,
1085 b"",
1086 b"",
1087 );
1088 return -1;
1089 }
1090 }
1091 }
1092 0
1093}
1094
1095unsafe extern "C" fn shell_valid_error(_ctx: *mut c_void, msg: *const c_char) {
1102 unsafe {
1103 if !msg.is_null() {
1104 out_cstr(stderr, msg);
1105 }
1106 }
1107}
1108
1109unsafe fn shell_parse_dtd(dtd: *const c_char) -> *mut _xmlDtd {
1117 if dtd.is_null() {
1118 return ptr::null_mut();
1119 }
1120 let path = match unsafe { core::ffi::CStr::from_ptr(dtd) }.to_str() {
1121 Ok(p) => p,
1122 Err(_) => return ptr::null_mut(),
1123 };
1124 let content = match std::fs::read(path) {
1125 Ok(c) => c,
1126 Err(_) => return ptr::null_mut(),
1127 };
1128 let lower: Vec<u8> = content.iter().map(|b| b.to_ascii_lowercase()).collect();
1130 let pos = match find_subslice(&lower, b"<!doctype") {
1131 Some(p) => p,
1132 None => {
1135 let base = path.rsplit(['/', '\\']).next().unwrap_or(path);
1136 let name_c = bytes_to_xmlstr(base.as_bytes());
1137 let dtd_node =
1138 crate::xml::dtd::new_dtd(ptr::null_mut(), name_c, ptr::null_mut(), ptr::null_mut());
1139 if !name_c.is_null() {
1140 xmlFreeImpl(name_c as *mut c_void);
1141 }
1142 return dtd_node;
1143 }
1144 };
1145 let mut i = pos + b"<!doctype".len();
1146 while i < content.len() && (content[i] as char).is_ascii_whitespace() {
1148 i += 1;
1149 }
1150 let name_start = i;
1152 while i < content.len()
1153 && !(content[i] as char).is_ascii_whitespace()
1154 && content[i] != b'>'
1155 && content[i] != b'['
1156 {
1157 i += 1;
1158 }
1159 if i == name_start {
1160 return ptr::null_mut();
1161 }
1162 let name = &content[name_start..i];
1163
1164 while i < content.len() && (content[i] as char).is_ascii_whitespace() {
1166 i += 1;
1167 }
1168 let mut public_id: Option<&[u8]> = None;
1169 let mut system_id: Option<&[u8]> = None;
1170 if i < content.len() && content[i..].to_ascii_lowercase().starts_with(b"public") {
1171 i += b"public".len();
1172 while i < content.len() && (content[i] as char).is_ascii_whitespace() {
1173 i += 1;
1174 }
1175 if i < content.len() && content[i] == b'"' {
1176 let s = i + 1;
1177 let e = content[s..].iter().position(|&c| c == b'"').map(|p| s + p);
1178 if let Some(e) = e {
1179 public_id = Some(&content[s..e]);
1180 }
1181 }
1182 while i < content.len() && (content[i] as char).is_ascii_whitespace() {
1183 i += 1;
1184 }
1185 if i < content.len() && content[i] == b'"' {
1186 let s = i + 1;
1187 let e = content[s..].iter().position(|&c| c == b'"').map(|p| s + p);
1188 if let Some(e) = e {
1189 system_id = Some(&content[s..e]);
1190 }
1191 }
1192 } else if i < content.len() && content[i..].to_ascii_lowercase().starts_with(b"system") {
1193 i += b"system".len();
1194 while i < content.len() && (content[i] as char).is_ascii_whitespace() {
1195 i += 1;
1196 }
1197 if i < content.len() && content[i] == b'"' {
1198 let s = i + 1;
1199 let e = content[s..].iter().position(|&c| c == b'"').map(|p| s + p);
1200 if let Some(e) = e {
1201 system_id = Some(&content[s..e]);
1202 }
1203 }
1204 }
1205
1206 let name_c = bytes_to_xmlstr(name);
1207 let ext_c = match public_id {
1208 Some(v) => bytes_to_xmlstr(v),
1209 None => ptr::null_mut(),
1210 };
1211 let sys_c = match system_id {
1212 Some(v) => bytes_to_xmlstr(v),
1213 None => ptr::null_mut(),
1214 };
1215 let dtd_node = crate::xml::dtd::new_dtd(ptr::null_mut(), name_c, ext_c, sys_c);
1216 if !name_c.is_null() {
1217 xmlFreeImpl(name_c as *mut c_void);
1218 }
1219 if !ext_c.is_null() {
1220 xmlFreeImpl(ext_c as *mut c_void);
1221 }
1222 if !sys_c.is_null() {
1223 xmlFreeImpl(sys_c as *mut c_void);
1224 }
1225 dtd_node
1226}
1227
1228fn find_subslice(haystack: &[u8], needle: &[u8]) -> Option<usize> {
1230 if needle.is_empty() || haystack.len() < needle.len() {
1231 return None;
1232 }
1233 haystack.windows(needle.len()).position(|w| w == needle)
1234}
1235
1236unsafe fn bytes_to_xmlstr(bytes: &[u8]) -> *mut xmlChar {
1238 let buf = xmlMallocImpl(bytes.len() + 1) as *mut xmlChar;
1239 if buf.is_null() {
1240 return ptr::null_mut();
1241 }
1242 unsafe {
1243 ptr::copy_nonoverlapping(bytes.as_ptr(), buf, bytes.len());
1244 *buf.add(bytes.len()) = 0;
1245 }
1246 buf
1247}
1248
1249#[no_mangle]
1267pub unsafe extern "C" fn xmlShellValidate(
1268 ctxt: *mut _xmlShellCtxt,
1269 dtd: *mut c_char,
1270 _node: *mut _xmlNode,
1271 _node2: *mut _xmlNode,
1272) -> c_int {
1273 if ctxt.is_null() || unsafe { (*ctxt).doc }.is_null() {
1274 return -1;
1275 }
1276 let vctxt = xmlMallocZero(size_of::<_xmlValidCtxt>()) as *mut _xmlValidCtxt;
1278 if vctxt.is_null() {
1279 return -1;
1280 }
1281 unsafe {
1282 (*vctxt).error = Some(shell_valid_error);
1283 (*vctxt).warning = Some(shell_valid_error);
1284 }
1285 let mut res = -1;
1286 unsafe {
1287 if dtd.is_null() || *dtd == 0 {
1288 res = xmlValidateDocument(vctxt, (*ctxt).doc);
1289 } else {
1290 let subset = shell_parse_dtd(dtd as *const c_char);
1291 if !subset.is_null() {
1292 res = xmlValidateDtd(vctxt, (*ctxt).doc, subset);
1293 crate::xml::dtd::free_dtd(subset);
1294 }
1295 }
1296 }
1297 unsafe {
1298 xmlFreeImpl(vctxt as *mut c_void);
1299 }
1300 res
1301}
1302
1303#[no_mangle]
1326pub unsafe extern "C" fn xmlShellDu(
1327 ctxt: *mut _xmlShellCtxt,
1328 _arg: *mut c_char,
1329 tree: *mut _xmlNode,
1330 _node2: *mut _xmlNode,
1331) -> c_int {
1332 if ctxt.is_null() {
1333 return -1;
1334 }
1335 if tree.is_null() {
1336 return -1;
1337 }
1338 let out = unsafe { (*ctxt).output };
1339 let mut indent: c_int = 0;
1340 let mut node: *mut _xmlNode = tree;
1341 unsafe {
1342 while !node.is_null() {
1343 let typ = (*node).type_;
1344 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
1345 || typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
1346 {
1347 out_bytes(out, b"/\n");
1348 } else if typ == xmlElementType::XML_ELEMENT_NODE as c_int {
1349 let mut line = Vec::new();
1350 for _ in 0..indent {
1351 line.extend_from_slice(b" ");
1352 }
1353 if !(*node).ns.is_null() && !(*(*node).ns).prefix.is_null() {
1354 push_cstr(&mut line, (*(*node).ns).prefix as *const c_char);
1355 line.push(b':');
1356 }
1357 push_cstr(&mut line, (*node).name as *const c_char);
1358 line.push(b'\n');
1359 out_bytes(out, &line);
1360 }
1361
1362 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
1366 || typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
1367 {
1368 node = (*(node as *mut _xmlDoc)).children;
1369 } else if !(*node).children.is_null()
1370 && typ != xmlElementType::XML_ENTITY_REF_NODE as c_int
1371 {
1372 node = (*node).children;
1373 indent += 1;
1374 } else if node != tree && !(*node).next.is_null() {
1375 node = (*node).next;
1376 } else if node != tree {
1377 while node != tree {
1378 if !(*node).parent.is_null() {
1379 node = (*node).parent;
1380 indent -= 1;
1381 }
1382 if node != tree && !(*node).next.is_null() {
1383 node = (*node).next;
1384 break;
1385 }
1386 if (*node).parent.is_null() {
1387 node = ptr::null_mut();
1388 break;
1389 }
1390 if node == tree {
1391 node = ptr::null_mut();
1392 break;
1393 }
1394 }
1395 if node == tree {
1396 node = ptr::null_mut();
1397 }
1398 } else {
1399 node = ptr::null_mut();
1400 }
1401 }
1402 }
1403 0
1404}
1405
1406#[no_mangle]
1425pub unsafe extern "C" fn xmlShellPwd(
1426 _ctxt: *mut _xmlShellCtxt,
1427 buffer: *mut c_char,
1428 node: *mut _xmlNode,
1429 _node2: *mut _xmlNode,
1430) -> c_int {
1431 if node.is_null() || buffer.is_null() {
1432 return -1;
1433 }
1434
1435 let path = unsafe { shell_get_node_path(node) };
1436 if path.is_null() {
1437 return -1;
1438 }
1439
1440 let plen = unsafe { tree::xml_strlen(path) } as usize;
1442 let n = plen.min(498);
1443 unsafe {
1444 ptr::copy_nonoverlapping(path as *const u8, buffer as *mut u8, n);
1445 *buffer.add(n) = 0;
1446 *buffer.add(499) = b'0' as c_char;
1447 }
1448 unsafe {
1449 xmlFreeImpl(path as *mut c_void);
1450 }
1451 0
1452}
1453
1454unsafe fn xmlShellSetBase(ctxt: *mut _xmlShellCtxt, arg: *mut c_char, node: *mut _xmlNode) {
1460 let _ = ctxt;
1461 if !node.is_null() {
1462 unsafe {
1463 xmlNodeSetBase(node, arg as *const xmlChar);
1464 }
1465 }
1466}
1467
1468unsafe fn xmlShellRegisterNamespace(ctxt: *mut _xmlShellCtxt, arg: *mut c_char) -> c_int {
1471 let ns_list_dup = unsafe { xmlStrdup(arg as *const xmlChar) };
1472 if ns_list_dup.is_null() {
1473 return -1;
1474 }
1475 let mut next: *mut xmlChar = ns_list_dup;
1476 loop {
1477 if unsafe { *next == 0 } {
1478 break;
1479 }
1480 let prefix = next;
1482 let eq = unsafe { xmlStrchr(next, b'=' as xmlChar) };
1483 if eq.is_null() {
1484 unsafe {
1485 out_cstr(
1486 (*ctxt).output,
1487 c"setns: prefix=[nsuri] required\n".as_ptr() as *const c_char,
1488 );
1489 }
1490 unsafe {
1491 xmlFreeImpl(ns_list_dup as *mut c_void);
1492 }
1493 return -1;
1494 }
1495 unsafe {
1497 *(eq as *mut xmlChar) = 0;
1498 }
1499 let href = unsafe { eq.add(1) };
1500 let space = unsafe { xmlStrchr(href, b' ' as xmlChar) };
1502 if !space.is_null() {
1503 unsafe {
1504 *(space as *mut xmlChar) = 0;
1505 }
1506 next = unsafe { space.add(1) as *mut xmlChar };
1507 } else {
1508 next = unsafe { href.add(tree::xml_strlen(href) as usize) as *mut xmlChar };
1509 }
1510
1511 if unsafe { xmlXPathRegisterNs((*ctxt).pctxt, prefix, href) } != 0 {
1513 unsafe {
1514 let mut msg = Vec::new();
1515 msg.extend_from_slice(b"Error: unable to register NS with prefix=\"");
1516 push_cstr(&mut msg, prefix as *const c_char);
1517 msg.extend_from_slice(b"\" and href=\"");
1518 push_cstr(&mut msg, href as *const c_char);
1519 msg.extend_from_slice(b"\"\n");
1520 out_bytes((*ctxt).output, &msg);
1521 }
1522 unsafe {
1523 xmlFreeImpl(ns_list_dup as *mut c_void);
1524 }
1525 return -1;
1526 }
1527 }
1528 unsafe {
1529 xmlFreeImpl(ns_list_dup as *mut c_void);
1530 }
1531 0
1532}
1533
1534unsafe fn xmlShellRegisterRootNamespaces(ctxt: *mut _xmlShellCtxt, root: *mut _xmlNode) -> c_int {
1537 if root.is_null()
1538 || unsafe { (*root).type_ != xmlElementType::XML_ELEMENT_NODE as c_int }
1539 || unsafe { (*root).nsDef.is_null() }
1540 || ctxt.is_null()
1541 || unsafe { (*ctxt).pctxt.is_null() }
1542 {
1543 return -1;
1544 }
1545 let mut ns = unsafe { (*root).nsDef };
1546 while !ns.is_null() {
1547 if unsafe { (*ns).prefix.is_null() } {
1548 unsafe {
1549 xmlXPathRegisterNs(
1550 (*ctxt).pctxt,
1551 c"defaultns".as_ptr() as *const xmlChar,
1552 (*ns).href,
1553 );
1554 }
1555 } else {
1556 unsafe {
1557 xmlXPathRegisterNs((*ctxt).pctxt, (*ns).prefix, (*ns).href);
1558 }
1559 }
1560 ns = unsafe { (*ns).next };
1561 }
1562 0
1563}
1564
1565unsafe fn xmlShellGrep(ctxt: *mut _xmlShellCtxt, arg: *mut c_char, node: *mut _xmlNode) {
1568 if ctxt.is_null() || node.is_null() || arg.is_null() {
1569 return;
1570 }
1571 let mut node = node;
1572 while !node.is_null() {
1573 unsafe {
1574 let typ = (*node).type_;
1575 if typ == xmlElementType::XML_COMMENT_NODE as c_int {
1576 if !xmlStrstr((*node).content, arg as *const xmlChar).is_null() {
1577 let path = shell_get_node_path(node);
1578 if !path.is_null() {
1579 let mut line = Vec::new();
1580 push_cstr(&mut line, path as *const c_char);
1581 line.extend_from_slice(b" : ");
1582 out_bytes((*ctxt).output, &line);
1583 xmlFreeImpl(path as *mut c_void);
1584 }
1585 xmlShellList(ctxt, ptr::null_mut(), node, ptr::null_mut());
1586 }
1587 } else if typ == xmlElementType::XML_TEXT_NODE as c_int
1588 && !xmlStrstr((*node).content, arg as *const xmlChar).is_null()
1589 {
1590 let path = shell_get_node_path((*node).parent);
1591 if !path.is_null() {
1592 let mut line = Vec::new();
1593 push_cstr(&mut line, path as *const c_char);
1594 line.extend_from_slice(b" : ");
1595 out_bytes((*ctxt).output, &line);
1596 xmlFreeImpl(path as *mut c_void);
1597 }
1598 xmlShellList(ctxt, ptr::null_mut(), (*node).parent, ptr::null_mut());
1599 }
1600
1601 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
1605 || typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
1606 {
1607 node = (*(node as *mut _xmlDoc)).children;
1608 } else if !(*node).children.is_null()
1609 && typ != xmlElementType::XML_ENTITY_REF_NODE as c_int
1610 {
1611 node = (*node).children;
1612 } else if !(*node).next.is_null() {
1613 node = (*node).next;
1614 } else {
1615 while !node.is_null() {
1616 if !(*node).parent.is_null() {
1617 node = (*node).parent;
1618 }
1619 if !(*node).next.is_null() {
1620 node = (*node).next;
1621 break;
1622 }
1623 if (*node).parent.is_null() {
1624 node = ptr::null_mut();
1625 break;
1626 }
1627 }
1628 }
1629 }
1630 }
1631}
1632
1633unsafe fn shell_result_type_error(arg: *const c_char, typ: c_int) {
1636 if typ == xmlXPathObjectType::XPATH_UNDEFINED as c_int {
1637 unsafe { shell_generic_error(arg, b": ", b"no such node\n") };
1638 } else if typ == xmlXPathObjectType::XPATH_BOOLEAN as c_int {
1639 unsafe { shell_generic_error(arg, b" is a Boolean", b"\n") };
1640 } else if typ == xmlXPathObjectType::XPATH_NUMBER as c_int {
1641 unsafe { shell_generic_error(arg, b" is a number", b"\n") };
1642 } else if typ == xmlXPathObjectType::XPATH_STRING as c_int {
1643 unsafe { shell_generic_error(arg, b" is a string", b"\n") };
1644 } else if typ == xmlXPathObjectType::XPATH_POINT as c_int {
1645 unsafe { shell_generic_error(arg, b" is a point", b"\n") };
1646 } else if typ == xmlXPathObjectType::XPATH_RANGE as c_int
1647 || typ == xmlXPathObjectType::XPATH_LOCATIONSET as c_int
1648 {
1649 unsafe { shell_generic_error(arg, b" is a range", b"\n") };
1650 } else if typ == xmlXPathObjectType::XPATH_USERS as c_int {
1651 unsafe { shell_generic_error(arg, b" is user-defined", b"\n") };
1652 } else if typ == xmlXPathObjectType::XPATH_XSLT_TREE as c_int {
1653 unsafe { shell_generic_error(arg, b" is an XSLT value tree", b"\n") };
1654 }
1655}
1656
1657unsafe fn shell_build_prompt(ctxt: *mut _xmlShellCtxt) -> Vec<u8> {
1664 let mut p = Vec::new();
1665 let node = unsafe { (*ctxt).node };
1666 let doc = unsafe { (*ctxt).doc };
1667 if node == doc as *mut _xmlNode {
1668 p.extend_from_slice(b"/ > ");
1669 } else if !node.is_null() && !unsafe { (*node).name }.is_null() {
1670 unsafe {
1671 if !(*node).ns.is_null() && !(*(*node).ns).prefix.is_null() {
1672 push_cstr(&mut p, (*(*node).ns).prefix as *const c_char);
1673 p.push(b':');
1674 }
1675 push_cstr(&mut p, (*node).name as *const c_char);
1676 }
1677 p.extend_from_slice(b" > ");
1678 } else {
1679 p.extend_from_slice(b"? > ");
1680 }
1681 p.push(0);
1682 p
1683}
1684
1685unsafe fn shell_print_help(ctxt: *mut _xmlShellCtxt) {
1687 let out = unsafe { (*ctxt).output };
1688 const HELP: &[&[u8]] = &[
1689 b"\tbase display XML base of the node\n",
1690 b"\tsetbase URI change the XML base of the node\n",
1691 b"\tbye leave shell\n",
1692 b"\tcat [node] display node or current node\n",
1693 b"\tcd [path] change directory to path or to root\n",
1694 b"\tdir [path] dumps information about the node (namespace, attributes, content)\n",
1695 b"\tdu [path] show the structure of the subtree under path or the current node\n",
1696 b"\texit leave shell\n",
1697 b"\thelp display this help\n",
1698 b"\tfree display memory usage\n",
1699 b"\tload [name] load a new document with name\n",
1700 b"\tls [path] list contents of path or the current directory\n",
1701 b"\txpath expr evaluate the XPath expression in that context and print the result\n",
1702 b"\tsetns nsreg register a namespace to a prefix in the XPath evaluation context\n",
1703 b"\t format for nsreg is: prefix=[nsuri] (i.e. prefix= unsets a prefix)\n",
1704 b"\tsetrootns register all namespace found on the root element\n",
1705 b"\t the default namespace if any uses 'defaultns' prefix\n",
1706 b"\tpwd display current working directory\n",
1707 b"\twhereis display absolute path of [path] or current working directory\n",
1708 b"\tquit leave shell\n",
1709 b"\tsave [name] save this document to name or the original name\n",
1710 b"\twrite [name] write the current node to the filename\n",
1711 b"\tvalidate check the document for errors\n",
1712 b"\tgrep string search for a string in the subtree\n",
1713 ];
1714 for line in HELP {
1715 unsafe {
1716 out_bytes(out, line);
1717 }
1718 }
1719}
1720
1721#[no_mangle]
1740pub unsafe extern "C" fn xmlShell(
1741 doc: *mut _xmlDoc,
1742 filename: *mut c_char,
1743 input: xmlShellReadlineFunc,
1744 output: *mut c_void,
1745) {
1746 if doc.is_null() || filename.is_null() || input.is_none() {
1747 return;
1748 }
1749 let output = if output.is_null() {
1750 unsafe { stdout }
1751 } else {
1752 output
1753 };
1754
1755 let ctxt = xmlMallocZero(size_of::<_xmlShellCtxt>()) as *mut _xmlShellCtxt;
1756 if ctxt.is_null() {
1757 return;
1758 }
1759 unsafe {
1760 (*ctxt).loaded = 0;
1761 (*ctxt).doc = doc;
1762 (*ctxt).input = input;
1763 (*ctxt).output = output;
1764 (*ctxt).filename = xmlStrdup(filename as *const xmlChar) as *mut c_char;
1765 (*ctxt).node = doc as *mut _xmlNode;
1766 (*ctxt).pctxt = xmlXPathNewContext(doc);
1767 }
1768 if unsafe { (*ctxt).pctxt }.is_null() {
1769 unsafe {
1770 xmlFreeImpl(ctxt as *mut c_void);
1771 }
1772 return;
1773 }
1774
1775 let mut cmdline: *mut c_char = ptr::null_mut();
1776 loop {
1777 let prompt = unsafe { shell_build_prompt(ctxt) };
1779 let readline = unsafe { (*ctxt).input };
1780 cmdline = match readline {
1781 Some(f) => f(prompt.as_ptr() as *mut c_char),
1782 None => break,
1783 };
1784 if cmdline.is_null() {
1785 break;
1786 }
1787
1788 let clen = unsafe { libc::strlen(cmdline) } as usize;
1790 let cbytes = unsafe { core::slice::from_raw_parts(cmdline as *const u8, clen) };
1791 let mut i = 0usize;
1792 while i < clen && (cbytes[i] == b' ' || cbytes[i] == b'\t') {
1793 i += 1;
1794 }
1795 let mut command: Vec<u8> = Vec::new();
1796 while i < clen
1797 && cbytes[i] != b' '
1798 && cbytes[i] != b'\t'
1799 && cbytes[i] != b'\n'
1800 && cbytes[i] != b'\r'
1801 {
1802 command.push(cbytes[i]);
1803 i += 1;
1804 }
1805 if command.is_empty() {
1806 unsafe {
1807 libc::free(cmdline as *mut c_void);
1808 }
1809 cmdline = ptr::null_mut();
1810 continue;
1811 }
1812
1813 while i < clen && (cbytes[i] == b' ' || cbytes[i] == b'\t') {
1815 i += 1;
1816 }
1817 let mut arg: Vec<u8> = Vec::new();
1818 while i < clen && cbytes[i] != b'\n' && cbytes[i] != b'\r' {
1819 arg.push(cbytes[i]);
1820 i += 1;
1821 }
1822
1823 command.push(0);
1825 let cmd: &[u8] = &command;
1826 let mut argn = arg.clone();
1827 argn.push(0);
1828 let arg_cstr: *mut c_char = argn.as_mut_ptr() as *mut c_char;
1829 let arg_xml: *const xmlChar = argn.as_ptr() as *const xmlChar;
1830
1831 if cmd == b"exit\0" || cmd == b"quit\0" || cmd == b"bye\0" {
1833 break;
1834 }
1835 if cmd == b"help\0" {
1836 unsafe { shell_print_help(ctxt) };
1837 } else if cmd == b"validate\0" {
1838 unsafe {
1839 xmlShellValidate(ctxt, arg_cstr, ptr::null_mut(), ptr::null_mut());
1840 }
1841 } else if cmd == b"load\0" {
1842 unsafe {
1843 xmlShellLoad(ctxt, arg_cstr, ptr::null_mut(), ptr::null_mut());
1844 }
1845 } else if cmd == b"save\0" {
1846 unsafe {
1847 xmlShellSave(ctxt, arg_cstr, ptr::null_mut(), ptr::null_mut());
1848 }
1849 } else if cmd == b"write\0" {
1850 if arg.is_empty() {
1851 unsafe {
1852 shell_generic_error(
1853 c"Write command requires a filename argument\n".as_ptr() as *const c_char,
1854 b"",
1855 b"",
1856 );
1857 }
1858 } else {
1859 unsafe {
1860 xmlShellWrite(ctxt, arg_cstr, (*ctxt).node, ptr::null_mut());
1861 }
1862 }
1863 } else if cmd == b"grep\0" {
1864 unsafe {
1865 xmlShellGrep(ctxt, arg_cstr, (*ctxt).node);
1866 }
1867 } else if cmd == b"free\0" {
1868 unsafe {
1869 if arg.is_empty() {
1870 crate::abi::allocator::xmlMemShow((*ctxt).output, 0);
1871 } else {
1872 let mut len: c_int = 0;
1873 let arg_s = core::str::from_utf8(&argn[..argn.len() - 1]).unwrap_or("");
1874 if let Ok(v) = arg_s.trim().parse::<c_int>() {
1875 len = v;
1876 }
1877 crate::abi::allocator::xmlMemShow((*ctxt).output, len);
1878 }
1879 }
1880 } else if cmd == b"pwd\0" {
1881 let mut dir = [0 as c_char; 500];
1882 unsafe {
1883 if xmlShellPwd(ctxt, dir.as_mut_ptr(), (*ctxt).node, ptr::null_mut()) == 0 {
1884 let mut line = Vec::new();
1885 push_cstr(&mut line, dir.as_mut_ptr());
1886 line.extend_from_slice(b"\n");
1887 out_bytes((*ctxt).output, &line);
1888 }
1889 }
1890 } else if cmd == b"du\0" {
1891 unsafe {
1892 if arg.is_empty() {
1893 xmlShellDu(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
1894 } else {
1895 (*(*ctxt).pctxt).node = (*ctxt).node;
1896 let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
1897 if !list.is_null() {
1898 let typ = (*list).type_;
1899 if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
1900 let ns = (*list).nodesetval as *mut _xmlNodeSet;
1901 if !ns.is_null() {
1902 for indx in 0..(*ns).nodeNr {
1903 let n = *(*ns).nodeTab.add(indx as usize);
1904 xmlShellDu(ctxt, ptr::null_mut(), n, ptr::null_mut());
1905 }
1906 }
1907 } else {
1908 shell_result_type_error(arg_cstr, typ);
1909 }
1910 xmlXPathFreeObject(list);
1911 } else {
1912 shell_generic_error(arg_cstr, b": ", b"no such node\n");
1913 }
1914 (*(*ctxt).pctxt).node = ptr::null_mut();
1915 }
1916 }
1917 } else if cmd == b"base\0" {
1918 unsafe {
1919 xmlShellBase(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
1920 }
1921 } else if cmd == b"setns\0" {
1922 unsafe {
1923 if arg.is_empty() {
1924 shell_generic_error(
1925 c"setns: prefix=[nsuri] required\n".as_ptr() as *const c_char,
1926 b"",
1927 b"",
1928 );
1929 } else {
1930 xmlShellRegisterNamespace(ctxt, arg_cstr);
1931 }
1932 }
1933 } else if cmd == b"setrootns\0" {
1934 unsafe {
1935 let root = tree::doc_get_root_element((*ctxt).doc);
1936 xmlShellRegisterRootNamespaces(ctxt, root);
1937 }
1938 } else if cmd == b"xpath\0" {
1939 unsafe {
1940 if arg.is_empty() {
1941 shell_generic_error(
1942 c"xpath: expression required\n".as_ptr() as *const c_char,
1943 b"",
1944 b"",
1945 );
1946 } else {
1947 (*(*ctxt).pctxt).node = (*ctxt).node;
1948 let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
1949 xmlXPathDebugDumpObject((*ctxt).output, list, 0);
1950 xmlXPathFreeObject(list);
1951 }
1952 }
1953 } else if cmd == b"setbase\0" {
1954 unsafe {
1955 xmlShellSetBase(ctxt, arg_cstr, (*ctxt).node);
1956 }
1957 } else if cmd == b"ls\0" || cmd == b"dir\0" {
1958 let is_dir = cmd == b"dir\0";
1959 unsafe {
1960 if arg.is_empty() {
1961 if is_dir {
1962 xmlShellDir(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
1963 } else {
1964 xmlShellList(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
1965 }
1966 } else {
1967 (*(*ctxt).pctxt).node = (*ctxt).node;
1968 let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
1969 if !list.is_null() {
1970 let typ = (*list).type_;
1971 if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
1972 let ns = (*list).nodesetval as *mut _xmlNodeSet;
1973 if !ns.is_null() {
1974 for indx in 0..(*ns).nodeNr {
1975 let n = *(*ns).nodeTab.add(indx as usize);
1976 if is_dir {
1977 xmlShellDir(ctxt, ptr::null_mut(), n, ptr::null_mut());
1978 } else {
1979 xmlShellList(ctxt, ptr::null_mut(), n, ptr::null_mut());
1980 }
1981 }
1982 }
1983 } else {
1984 shell_result_type_error(arg_cstr, typ);
1985 }
1986 xmlXPathFreeObject(list);
1987 } else {
1988 shell_generic_error(arg_cstr, b": ", b"no such node\n");
1989 }
1990 (*(*ctxt).pctxt).node = ptr::null_mut();
1991 }
1992 }
1993 } else if cmd == b"whereis\0" {
1994 let mut dir = [0 as c_char; 500];
1995 unsafe {
1996 if arg.is_empty() {
1997 if xmlShellPwd(ctxt, dir.as_mut_ptr(), (*ctxt).node, ptr::null_mut()) == 0 {
1998 let mut line = Vec::new();
1999 push_cstr(&mut line, dir.as_mut_ptr());
2000 line.extend_from_slice(b"\n");
2001 out_bytes((*ctxt).output, &line);
2002 }
2003 } else {
2004 (*(*ctxt).pctxt).node = (*ctxt).node;
2005 let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
2006 if !list.is_null() {
2007 let typ = (*list).type_;
2008 if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
2009 let ns = (*list).nodesetval as *mut _xmlNodeSet;
2010 if !ns.is_null() {
2011 for indx in 0..(*ns).nodeNr {
2012 let n = *(*ns).nodeTab.add(indx as usize);
2013 if xmlShellPwd(ctxt, dir.as_mut_ptr(), n, ptr::null_mut()) == 0
2014 {
2015 let mut line = Vec::new();
2016 push_cstr(&mut line, dir.as_mut_ptr());
2017 line.extend_from_slice(b"\n");
2018 out_bytes((*ctxt).output, &line);
2019 }
2020 }
2021 }
2022 } else {
2023 shell_result_type_error(arg_cstr, typ);
2024 }
2025 xmlXPathFreeObject(list);
2026 } else {
2027 shell_generic_error(arg_cstr, b": ", b"no such node\n");
2028 }
2029 (*(*ctxt).pctxt).node = ptr::null_mut();
2030 }
2031 }
2032 } else if cmd == b"cd\0" {
2033 unsafe {
2034 if arg.is_empty() {
2035 (*ctxt).node = (*ctxt).doc as *mut _xmlNode;
2036 } else {
2037 let mut argn = argn;
2039 let l = argn.len();
2040 if l >= 3 && argn[l - 2] == b'/' {
2041 argn[l - 2] = 0;
2042 }
2043 (*(*ctxt).pctxt).node = (*ctxt).node;
2044 let list = xmlXPathEval(argn.as_ptr() as *const xmlChar, (*ctxt).pctxt);
2045 if !list.is_null() {
2046 let typ = (*list).type_;
2047 if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
2048 let ns = (*list).nodesetval as *mut _xmlNodeSet;
2049 if !ns.is_null() {
2050 if (*ns).nodeNr == 1 {
2051 (*ctxt).node = *(*ns).nodeTab;
2052 if !(*ctxt).node.is_null()
2053 && (*(*ctxt).node).type_
2054 == xmlElementType::XML_NAMESPACE_DECL as c_int
2055 {
2056 shell_generic_error(
2057 c"cannot cd to namespace\n".as_ptr() as *const c_char,
2058 b"",
2059 b"",
2060 );
2061 (*ctxt).node = ptr::null_mut();
2062 }
2063 } else {
2064 let mut msg = Vec::new();
2065 push_cstr(&mut msg, arg_cstr);
2066 msg.extend_from_slice(b" is a ");
2067 msg.extend_from_slice((*ns).nodeNr.to_string().as_bytes());
2068 msg.extend_from_slice(b" Node Set\n");
2069 out_bytes(stderr, &msg);
2070 }
2071 } else {
2072 let mut msg = Vec::new();
2073 push_cstr(&mut msg, arg_cstr);
2074 msg.extend_from_slice(b" is an empty Node Set\n");
2075 out_bytes(stderr, &msg);
2076 }
2077 } else {
2078 shell_result_type_error(arg_cstr, typ);
2079 }
2080 xmlXPathFreeObject(list);
2081 } else {
2082 shell_generic_error(arg_cstr, b": ", b"no such node\n");
2083 }
2084 (*(*ctxt).pctxt).node = ptr::null_mut();
2085 }
2086 }
2087 } else if cmd == b"cat\0" {
2088 unsafe {
2089 if arg.is_empty() {
2090 xmlShellCat(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
2091 } else {
2092 (*(*ctxt).pctxt).node = (*ctxt).node;
2097 let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
2098 if !list.is_null() {
2099 let typ = (*list).type_;
2100 if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
2101 let ns = (*list).nodesetval as *mut _xmlNodeSet;
2102 if !ns.is_null() {
2103 for indx in 0..(*ns).nodeNr {
2104 if i > 0 {
2105 out_bytes((*ctxt).output, b" -------\n");
2106 }
2107 let n = *(*ns).nodeTab.add(indx as usize);
2108 xmlShellCat(ctxt, ptr::null_mut(), n, ptr::null_mut());
2109 }
2110 }
2111 } else {
2112 shell_result_type_error(arg_cstr, typ);
2113 }
2114 xmlXPathFreeObject(list);
2115 } else {
2116 shell_generic_error(arg_cstr, b": ", b"no such node\n");
2117 }
2118 (*(*ctxt).pctxt).node = ptr::null_mut();
2119 }
2120 }
2121 } else {
2122 let mut msg = Vec::new();
2123 msg.extend_from_slice(b"Unknown command ");
2124 msg.extend_from_slice(&command[..command.len() - 1]);
2125 msg.extend_from_slice(b"\n");
2126 unsafe {
2127 out_bytes(stderr, &msg);
2128 }
2129 }
2130
2131 unsafe {
2132 libc::free(cmdline as *mut c_void);
2133 }
2134 cmdline = ptr::null_mut();
2135 }
2136
2137 unsafe {
2139 xmlXPathFreeContext((*ctxt).pctxt);
2140 if (*ctxt).loaded != 0 {
2141 tree::free_doc((*ctxt).doc);
2142 }
2143 if !(*ctxt).filename.is_null() {
2144 xmlFreeImpl((*ctxt).filename as *mut c_void);
2145 }
2146 xmlFreeImpl(ctxt as *mut c_void);
2147 if !cmdline.is_null() {
2148 libc::free(cmdline as *mut c_void);
2149 }
2150 }
2151}