1#![allow(
105 missing_docs,
106 missing_debug_implementations,
107 non_snake_case,
108 non_camel_case_types,
109 non_upper_case_globals
110)]
111
112use core::ffi::c_void;
123use core::ptr;
124use std::os::raw::{c_char, c_int};
125
126use crate::abi::allocator::{xmlFreeImpl, xmlMallocImpl, xmlMallocZero};
127use crate::abi::exports_string::xmlStrstr;
128use crate::abi::exports_tree::{xmlNodeGetBase, xmlNodeSetBase};
129use crate::abi::exports_uri::xmlCanonicPath;
130use crate::abi::exports_xml2::{
131 xmlReadFile, xmlSaveFile, xmlStrEqual, xmlStrchr, xmlStrdup, xmlValidateDocument,
132 xmlValidateDtd, xmlXPathEval, xmlXPathFreeContext, xmlXPathFreeObject, xmlXPathNewContext,
133 xmlXPathRegisterNs,
134};
135use crate::abi::structs::{
136 _xmlAttr, _xmlDoc, _xmlDtd, _xmlNode, _xmlNodeSet, _xmlValidCtxt, _xmlXPathContext,
137 _xmlXPathObject,
138};
139use crate::abi::types::{xmlChar, xmlElementType, xmlXPathObjectType};
140use crate::xml::xpath::exports::xmlXPathDebugDumpObject;
141use crate::xml::{debug, io, tree};
142
143pub type xmlShellReadlineFunc = Option<unsafe extern "C" fn(prompt: *mut c_char) -> *mut c_char>;
150
151#[repr(C)]
153pub struct _xmlShellCtxt {
154 pub filename: *mut c_char,
156 pub doc: *mut _xmlDoc,
158 pub node: *mut _xmlNode,
160 pub pctxt: *mut _xmlXPathContext,
162 pub loaded: c_int,
164 pub output: *mut c_void,
166 pub input: xmlShellReadlineFunc,
168}
169
170pub type xmlShellCmd = Option<
173 unsafe extern "C" fn(*mut _xmlShellCtxt, *mut c_char, *mut _xmlNode, *mut _xmlNode) -> c_int,
174>;
175
176extern "C" {
177 fn fwrite(ptr: *const c_void, size: usize, nmemb: usize, stream: *mut c_void) -> usize;
179 fn fputs(s: *const c_char, stream: *mut c_void) -> c_int;
181 static mut stdout: *mut c_void;
183 static mut stderr: *mut c_void;
185}
186
187unsafe fn push_cstr(v: &mut Vec<u8>, s: *const c_char) {
193 if s.is_null() {
194 return;
195 }
196 let len = libc::strlen(s);
197 v.extend_from_slice(core::slice::from_raw_parts(s as *const u8, len));
198}
199
200unsafe fn out_bytes(fp: *mut c_void, bytes: &[u8]) {
202 if fp.is_null() || bytes.is_empty() {
203 return;
204 }
205 unsafe {
206 fwrite(bytes.as_ptr() as *const c_void, 1, bytes.len(), fp);
207 }
208}
209
210unsafe fn out_cstr(fp: *mut c_void, s: *const c_char) {
212 if fp.is_null() || s.is_null() {
213 return;
214 }
215 unsafe {
216 fputs(s, fp);
217 }
218}
219
220unsafe fn shell_generic_error(arg: *const c_char, mid: &[u8], end: &[u8]) {
226 let mut v = Vec::new();
227 unsafe {
228 push_cstr(&mut v, arg);
229 }
230 v.extend_from_slice(mid);
231 v.extend_from_slice(end);
232 unsafe {
233 out_bytes(stderr, &v);
234 }
235}
236
237unsafe fn shell_elem_dump(fp: *mut c_void, doc: *mut _xmlDoc, node: *mut _xmlNode) -> c_int {
241 if fp.is_null() || node.is_null() {
242 return -1;
243 }
244 let buf = io::buf_create(-1);
245 if buf.is_null() {
246 return -1;
247 }
248 let ret = tree::node_dump(buf, doc, node, 0, 0);
249 if ret < 0 {
250 io::buf_free(buf);
251 return -1;
252 }
253 let content = io::buf_content(buf);
254 let len = io::buf_length(buf);
255 if !content.is_null() && len > 0 {
256 unsafe {
257 fwrite(content as *const c_void, 1, len as usize, fp);
258 }
259 }
260 io::buf_free(buf);
261 0
262}
263
264unsafe fn shell_html_doc_dump(fp: *mut c_void, doc: *mut _xmlDoc) -> c_int {
266 if fp.is_null() || doc.is_null() {
267 return -1;
268 }
269 let buf = io::buf_create(-1);
270 if buf.is_null() {
271 return -1;
272 }
273 let ret = crate::xml::html::doc_dump(buf, doc);
274 if ret < 0 {
275 io::buf_free(buf);
276 return -1;
277 }
278 let content = io::buf_content(buf);
279 let len = io::buf_length(buf);
280 if !content.is_null() && len > 0 {
281 unsafe {
282 fwrite(content as *const c_void, 1, len as usize, fp);
283 }
284 }
285 io::buf_free(buf);
286 ret
287}
288
289unsafe fn shell_html_node_dump_file(fp: *mut c_void, node: *mut _xmlNode) -> c_int {
291 if fp.is_null() || node.is_null() {
292 return -1;
293 }
294 let buf = io::buf_create(-1);
295 if buf.is_null() {
296 return -1;
297 }
298 let before = io::buf_length(buf);
299 crate::xml::html::serialize_node(node, buf, 0, 0);
300 let after = io::buf_length(buf);
301 if after < 0 || before < 0 {
302 io::buf_free(buf);
303 return -1;
304 }
305 let content = io::buf_content(buf);
306 let len = io::buf_length(buf);
307 if !content.is_null() && len > 0 {
308 unsafe {
309 fwrite(content as *const c_void, 1, len as usize, fp);
310 }
311 }
312 io::buf_free(buf);
313 after - before
314}
315
316unsafe fn shell_get_node_path(node: *const _xmlNode) -> *mut xmlChar {
319 if node.is_null() || (*node).type_ == xmlElementType::XML_NAMESPACE_DECL as c_int {
320 return ptr::null_mut();
321 }
322
323 let mut segments: Vec<Vec<u8>> = Vec::new();
326 let mut cur: *const _xmlNode = node;
327
328 loop {
329 if cur.is_null() {
330 break;
331 }
332 let typ = (*cur).type_;
333 let mut seg: Vec<u8> = Vec::new();
334 let mut occur: c_int = 0;
335 let mut generic: bool;
336 let next: *const _xmlNode;
337
338 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
339 || typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
340 {
341 if !segments.is_empty() {
344 break;
345 }
346 seg.extend_from_slice(b"/");
347 next = ptr::null();
348 } else if typ == xmlElementType::XML_ELEMENT_NODE as c_int {
349 generic = false;
350 seg.extend_from_slice(b"/");
351 let name = (*cur).name;
352 let ns = (*cur).ns;
353 if !name.is_null() {
354 if !ns.is_null() && !(*ns).prefix.is_null() {
355 unsafe {
356 push_cstr(&mut seg, (*ns).prefix as *const c_char);
357 }
358 seg.push(b':');
359 unsafe {
360 push_cstr(&mut seg, name as *const c_char);
361 }
362 } else if !ns.is_null() {
363 generic = true;
365 seg.extend_from_slice(b"*");
366 } else {
367 unsafe {
368 push_cstr(&mut seg, name as *const c_char);
369 }
370 }
371 }
372 next = (*cur).parent;
373
374 let mut tmp = (*cur).prev;
376 while !tmp.is_null() {
377 if (*tmp).type_ == xmlElementType::XML_ELEMENT_NODE as c_int
378 && (generic || unsafe { shell_same_element_name(cur, tmp) })
379 {
380 occur += 1;
381 }
382 tmp = (*tmp).prev;
383 }
384 if occur == 0 {
385 let mut tmp = (*cur).next;
386 while !tmp.is_null() && occur == 0 {
387 if (*tmp).type_ == xmlElementType::XML_ELEMENT_NODE as c_int
388 && (generic || unsafe { shell_same_element_name(cur, tmp) })
389 {
390 occur += 1;
391 }
392 tmp = (*tmp).next;
393 }
394 if occur != 0 {
395 occur = 1;
396 }
397 } else {
398 occur += 1;
399 }
400 } else if typ == xmlElementType::XML_COMMENT_NODE as c_int {
401 seg.extend_from_slice(b"/comment()");
402 next = (*cur).parent;
403
404 let mut tmp = (*cur).prev;
405 while !tmp.is_null() {
406 if (*tmp).type_ == xmlElementType::XML_COMMENT_NODE as c_int {
407 occur += 1;
408 }
409 tmp = (*tmp).prev;
410 }
411 if occur == 0 {
412 let mut tmp = (*cur).next;
413 while !tmp.is_null() && occur == 0 {
414 if (*tmp).type_ == xmlElementType::XML_COMMENT_NODE as c_int {
415 occur += 1;
416 }
417 tmp = (*tmp).next;
418 }
419 if occur != 0 {
420 occur = 1;
421 }
422 } else {
423 occur += 1;
424 }
425 } else if typ == xmlElementType::XML_TEXT_NODE as c_int
426 || typ == xmlElementType::XML_CDATA_SECTION_NODE as c_int
427 {
428 seg.extend_from_slice(b"/text()");
429 next = (*cur).parent;
430
431 let mut tmp = (*cur).prev;
432 while !tmp.is_null() {
433 if (*tmp).type_ == xmlElementType::XML_TEXT_NODE as c_int
434 || (*tmp).type_ == xmlElementType::XML_CDATA_SECTION_NODE as c_int
435 {
436 occur += 1;
437 }
438 tmp = (*tmp).prev;
439 }
440 if occur == 0 {
441 let mut tmp = (*cur).next;
442 while !tmp.is_null() {
443 if (*tmp).type_ == xmlElementType::XML_TEXT_NODE as c_int
444 || (*tmp).type_ == xmlElementType::XML_CDATA_SECTION_NODE as c_int
445 {
446 occur = 1;
447 break;
448 }
449 tmp = (*tmp).next;
450 }
451 } else {
452 occur += 1;
453 }
454 } else if typ == xmlElementType::XML_PI_NODE as c_int {
455 let mut nm = Vec::new();
456 nm.extend_from_slice(b"processing-instruction('");
457 unsafe {
458 push_cstr(&mut nm, (*cur).name as *const c_char);
459 }
460 nm.extend_from_slice(b"')");
461 seg.extend_from_slice(b"/");
462 seg.extend_from_slice(&nm);
463 next = (*cur).parent;
464
465 let mut tmp = (*cur).prev;
466 while !tmp.is_null() {
467 if (*tmp).type_ == xmlElementType::XML_PI_NODE as c_int
468 && unsafe { xmlStrEqual((*cur).name, (*tmp).name) != 0 }
469 {
470 occur += 1;
471 }
472 tmp = (*tmp).prev;
473 }
474 if occur == 0 {
475 let mut tmp = (*cur).next;
476 while !tmp.is_null() && occur == 0 {
477 if (*tmp).type_ == xmlElementType::XML_PI_NODE as c_int
478 && unsafe { xmlStrEqual((*cur).name, (*tmp).name) != 0 }
479 {
480 occur += 1;
481 }
482 tmp = (*tmp).next;
483 }
484 if occur != 0 {
485 occur = 1;
486 }
487 } else {
488 occur += 1;
489 }
490 } else if typ == xmlElementType::XML_ATTRIBUTE_NODE as c_int {
491 seg.extend_from_slice(b"/@");
492 let attr = cur as *const _xmlAttr;
493 let name = (*attr).name;
494 if !name.is_null() {
495 if !(*attr).ns.is_null() && !(*(*attr).ns).prefix.is_null() {
496 unsafe {
497 push_cstr(&mut seg, (*(*attr).ns).prefix as *const c_char);
498 }
499 seg.push(b':');
500 }
501 unsafe {
502 push_cstr(&mut seg, name as *const c_char);
503 }
504 }
505 next = (*attr).parent;
506 } else {
507 return ptr::null_mut();
508 }
509
510 if occur != 0 {
511 seg.extend_from_slice(format!("[{}]", occur).as_bytes());
512 }
513 segments.push(seg);
514 if next.is_null() {
515 break;
516 }
517 cur = next;
518 }
519
520 let mut path: Vec<u8> = Vec::new();
522 for seg in segments.iter().rev() {
523 path.extend_from_slice(seg);
524 }
525 path.push(0);
526
527 let ret = xmlMallocImpl(path.len()) as *mut xmlChar;
528 if ret.is_null() {
529 return ptr::null_mut();
530 }
531 unsafe {
532 ptr::copy_nonoverlapping(path.as_ptr(), ret, path.len());
533 }
534 ret
535}
536
537unsafe fn shell_same_element_name(a: *const _xmlNode, b: *const _xmlNode) -> bool {
540 unsafe {
541 if xmlStrEqual((*a).name, (*b).name) == 0 {
542 return false;
543 }
544 let ans = (*a).ns;
545 let bns = (*b).ns;
546 if ans == bns {
547 return true;
548 }
549 if !ans.is_null() && !bns.is_null() {
550 return xmlStrEqual((*ans).prefix, (*bns).prefix) != 0;
551 }
552 false
553 }
554}
555
556#[no_mangle]
579pub unsafe extern "C" fn xmlShellPrintXPathError(errorType: c_int, arg: *const c_char) {
580 let default_arg = b"Result\0";
581 let arg = if arg.is_null() {
582 default_arg.as_ptr() as *const c_char
583 } else {
584 arg
585 };
586
587 if errorType == xmlXPathObjectType::XPATH_UNDEFINED as c_int {
588 unsafe { shell_generic_error(arg, b": ", b"no such node\n") };
589 } else if errorType == xmlXPathObjectType::XPATH_BOOLEAN as c_int {
590 unsafe { shell_generic_error(arg, b" is a Boolean", b"\n") };
591 } else if errorType == xmlXPathObjectType::XPATH_NUMBER as c_int {
592 unsafe { shell_generic_error(arg, b" is a number", b"\n") };
593 } else if errorType == xmlXPathObjectType::XPATH_STRING as c_int {
594 unsafe { shell_generic_error(arg, b" is a string", b"\n") };
595 } else if errorType == xmlXPathObjectType::XPATH_POINT as c_int {
596 unsafe { shell_generic_error(arg, b" is a point", b"\n") };
597 } else if errorType == xmlXPathObjectType::XPATH_RANGE as c_int
598 || errorType == xmlXPathObjectType::XPATH_LOCATIONSET as c_int
599 {
600 unsafe { shell_generic_error(arg, b" is a range", b"\n") };
601 } else if errorType == xmlXPathObjectType::XPATH_USERS as c_int {
602 unsafe { shell_generic_error(arg, b" is user-defined", b"\n") };
603 } else if errorType == xmlXPathObjectType::XPATH_XSLT_TREE as c_int {
604 unsafe { shell_generic_error(arg, b" is an XSLT value tree", b"\n") };
605 }
606}
607
608unsafe fn xmlShellPrintNodeCtxt(ctxt: *mut _xmlShellCtxt, node: *mut _xmlNode) {
611 if node.is_null() {
612 return;
613 }
614 let fp = if ctxt.is_null() {
615 unsafe { stdout }
616 } else {
617 (*ctxt).output
618 };
619
620 let typ = unsafe { (*node).type_ };
621 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int {
622 tree::xmlDocDump(fp, node as *mut _xmlDoc);
623 } else if typ == xmlElementType::XML_ATTRIBUTE_NODE as c_int {
624 unsafe {
625 debug::xmlDebugDumpAttrList(fp as *mut debug::_IO_FILE, node as *mut _xmlAttr, 0);
626 }
627 } else {
628 unsafe {
629 shell_elem_dump(fp, (*node).doc, node);
630 }
631 }
632 unsafe {
633 out_bytes(fp, b"\n");
634 }
635}
636
637#[no_mangle]
654pub unsafe extern "C" fn xmlShellPrintNode(node: *mut _xmlNode) {
655 unsafe { xmlShellPrintNodeCtxt(ptr::null_mut(), node) };
656}
657
658#[no_mangle]
678pub unsafe extern "C" fn xmlShellPrintXPathResult(list: *mut _xmlXPathObject) {
679 unsafe {
680 xmlXPathDebugDumpObject(stdout, list, 0);
681 }
682}
683
684#[no_mangle]
706pub unsafe extern "C" fn xmlShellList(
707 ctxt: *mut _xmlShellCtxt,
708 _arg: *mut c_char,
709 node: *mut _xmlNode,
710 _node2: *mut _xmlNode,
711) -> c_int {
712 if ctxt.is_null() {
713 return 0;
714 }
715 if node.is_null() {
716 unsafe {
717 out_bytes((*ctxt).output, b"NULL\n");
718 }
719 return 0;
720 }
721 let typ = unsafe { (*node).type_ };
722 let mut cur: *mut _xmlNode;
723 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
724 || typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
725 {
726 cur = unsafe { (*(node as *mut _xmlDoc)).children };
727 } else if typ == xmlElementType::XML_NAMESPACE_DECL as c_int {
728 unsafe {
729 debug::xmlLsOneNode((*ctxt).output as *mut debug::_IO_FILE, node);
730 }
731 return 0;
732 } else if !unsafe { (*node).children }.is_null() {
733 cur = unsafe { (*node).children };
734 } else {
735 unsafe {
736 debug::xmlLsOneNode((*ctxt).output as *mut debug::_IO_FILE, node);
737 }
738 return 0;
739 }
740 while !cur.is_null() {
741 unsafe {
742 debug::xmlLsOneNode((*ctxt).output as *mut debug::_IO_FILE, cur);
743 cur = (*cur).next;
744 }
745 }
746 0
747}
748
749#[no_mangle]
767pub unsafe extern "C" fn xmlShellBase(
768 ctxt: *mut _xmlShellCtxt,
769 _arg: *mut c_char,
770 node: *mut _xmlNode,
771 _node2: *mut _xmlNode,
772) -> c_int {
773 if ctxt.is_null() {
774 return 0;
775 }
776 if node.is_null() {
777 unsafe {
778 out_bytes((*ctxt).output, b"NULL\n");
779 }
780 return 0;
781 }
782
783 let base = unsafe { xmlNodeGetBase((*node).doc, node) };
784
785 if base.is_null() {
786 unsafe {
787 out_bytes((*ctxt).output, b" No base found !!!\n");
788 }
789 } else {
790 unsafe {
791 out_cstr((*ctxt).output, base as *const c_char);
792 out_bytes((*ctxt).output, b"\n");
793 xmlFreeImpl(base as *mut c_void);
794 }
795 }
796 0
797}
798
799#[no_mangle]
817pub unsafe extern "C" fn xmlShellDir(
818 ctxt: *mut _xmlShellCtxt,
819 _arg: *mut c_char,
820 node: *mut _xmlNode,
821 _node2: *mut _xmlNode,
822) -> c_int {
823 if ctxt.is_null() {
824 return 0;
825 }
826 if node.is_null() {
827 unsafe {
828 out_bytes((*ctxt).output, b"NULL\n");
829 }
830 return 0;
831 }
832 let typ = unsafe { (*node).type_ };
833 unsafe {
834 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
835 || typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
836 {
837 debug::xmlDebugDumpDocumentHead(
838 (*ctxt).output as *mut debug::_IO_FILE,
839 node as *mut _xmlDoc,
840 );
841 } else if typ == xmlElementType::XML_ATTRIBUTE_NODE as c_int {
842 debug::xmlDebugDumpAttr(
843 (*ctxt).output as *mut debug::_IO_FILE,
844 node as *mut _xmlAttr,
845 0,
846 );
847 } else {
848 debug::xmlDebugDumpOneNode((*ctxt).output as *mut debug::_IO_FILE, node, 0);
849 }
850 }
851 0
852}
853
854#[no_mangle]
877pub unsafe extern "C" fn xmlShellCat(
878 ctxt: *mut _xmlShellCtxt,
879 _arg: *mut c_char,
880 node: *mut _xmlNode,
881 _node2: *mut _xmlNode,
882) -> c_int {
883 if ctxt.is_null() {
884 return 0;
885 }
886 if node.is_null() {
887 unsafe {
888 out_bytes((*ctxt).output, b"NULL\n");
889 }
890 return 0;
891 }
892 let out = unsafe { (*ctxt).output };
893 let is_html =
894 unsafe { (*(*ctxt).doc).type_ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int };
895 let typ = unsafe { (*node).type_ };
896 unsafe {
897 if is_html {
898 if typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int {
899 shell_html_doc_dump(out, node as *mut _xmlDoc);
900 } else {
901 shell_html_node_dump_file(out, node);
902 }
903 } else if typ == xmlElementType::XML_DOCUMENT_NODE as c_int {
904 tree::xmlDocDump(out, node as *mut _xmlDoc);
905 } else {
906 shell_elem_dump(out, (*ctxt).doc, node);
907 }
908 out_bytes(out, b"\n");
909 }
910 0
911}
912
913#[no_mangle]
931pub unsafe extern "C" fn xmlShellLoad(
932 ctxt: *mut _xmlShellCtxt,
933 filename: *mut c_char,
934 _node: *mut _xmlNode,
935 _node2: *mut _xmlNode,
936) -> c_int {
937 if ctxt.is_null() || filename.is_null() {
938 return -1;
939 }
940 let mut html = 0;
941 if !unsafe { (*ctxt).doc }.is_null() {
942 html = unsafe {
943 ((*(*ctxt).doc).type_ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int) as c_int
944 };
945 }
946
947 let doc: *mut _xmlDoc = if html != 0 {
948 unsafe { crate::xml::html::parse_file(filename, ptr::null(), 0) }
951 } else {
952 unsafe { xmlReadFile(filename, ptr::null(), 0) }
953 };
954
955 if !doc.is_null() {
956 unsafe {
957 if (*ctxt).loaded == 1 {
958 tree::free_doc((*ctxt).doc);
959 }
960 (*ctxt).loaded = 1;
961 xmlXPathFreeContext((*ctxt).pctxt);
962 if !(*ctxt).filename.is_null() {
963 xmlFreeImpl((*ctxt).filename as *mut c_void);
964 }
965 (*ctxt).doc = doc;
966 (*ctxt).node = doc as *mut _xmlNode;
967 (*ctxt).pctxt = xmlXPathNewContext(doc);
968 (*ctxt).filename = xmlCanonicPath(filename) as *mut c_char;
969 }
970 0
971 } else {
972 -1
973 }
974}
975
976#[no_mangle]
995pub unsafe extern "C" fn xmlShellWrite(
996 ctxt: *mut _xmlShellCtxt,
997 filename: *mut c_char,
998 node: *mut _xmlNode,
999 _node2: *mut _xmlNode,
1000) -> c_int {
1001 if node.is_null() {
1002 return -1;
1003 }
1004 if filename.is_null() || *filename == 0 {
1005 unsafe {
1006 shell_generic_error(
1007 c"Write command requires a filename argument\n".as_ptr() as *const c_char,
1008 b"",
1009 b"",
1010 );
1011 }
1012 return -1;
1013 }
1014 if libc::access(filename, libc::W_OK) != 0 {
1016 unsafe {
1017 shell_generic_error(c"Cannot write to ".as_ptr() as *const c_char, b"", b"");
1018 shell_generic_error(filename, b"", b"\n");
1019 }
1020 return -1;
1021 }
1022 let typ = unsafe { (*node).type_ };
1023 unsafe {
1024 match typ {
1025 t if t == xmlElementType::XML_DOCUMENT_NODE as c_int => {
1026 if xmlSaveFile(filename, (*ctxt).doc) < -1 {
1027 shell_generic_error(c"Failed to write to ".as_ptr() as *const c_char, b"", b"");
1028 shell_generic_error(filename, b"", b"\n");
1029 return -1;
1030 }
1031 }
1032 t if t == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int => {
1033 if shell_save_html_doc(filename, (*ctxt).doc) < 0 {
1035 shell_generic_error(c"Failed to write to ".as_ptr() as *const c_char, b"", b"");
1036 shell_generic_error(filename, b"", b"\n");
1037 return -1;
1038 }
1039 }
1040 _ => {
1041 let f = libc::fopen(filename, c"w".as_ptr() as *const c_char);
1042 if f.is_null() {
1043 shell_generic_error(c"Failed to write to ".as_ptr() as *const c_char, b"", b"");
1044 shell_generic_error(filename, b"", b"\n");
1045 return -1;
1046 }
1047 shell_elem_dump(f as *mut c_void, (*ctxt).doc, node);
1048 libc::fclose(f);
1049 }
1050 }
1051 }
1052 0
1053}
1054
1055unsafe fn shell_save_html_doc(filename: *const c_char, doc: *mut _xmlDoc) -> c_int {
1057 if filename.is_null() || doc.is_null() {
1058 return -1;
1059 }
1060 let buf = io::buf_create(-1);
1061 if buf.is_null() {
1062 return -1;
1063 }
1064 let ret = crate::xml::html::doc_dump(buf, doc);
1065 if ret < 0 {
1066 io::buf_free(buf);
1067 return -1;
1068 }
1069 let content = io::buf_content(buf);
1070 let len = io::buf_length(buf);
1071 let written = if !content.is_null() && len > 0 {
1072 let f = libc::fopen(filename, c"w".as_ptr() as *const c_char);
1073 if f.is_null() {
1074 io::buf_free(buf);
1075 return -1;
1076 }
1077 let n = libc::fwrite(content as *const c_void, 1, len as usize, f);
1078 libc::fclose(f);
1079 n as c_int
1080 } else {
1081 0
1082 };
1083 io::buf_free(buf);
1084 written
1085}
1086
1087#[no_mangle]
1106pub unsafe extern "C" fn xmlShellSave(
1107 ctxt: *mut _xmlShellCtxt,
1108 filename: *mut c_char,
1109 _node: *mut _xmlNode,
1110 _node2: *mut _xmlNode,
1111) -> c_int {
1112 if ctxt.is_null() || unsafe { (*ctxt).doc }.is_null() {
1113 return -1;
1114 }
1115 let mut filename = filename;
1116 if filename.is_null() || *filename == 0 {
1117 filename = unsafe { (*ctxt).filename };
1118 }
1119 if filename.is_null() {
1120 return -1;
1121 }
1122 if libc::access(filename, libc::W_OK) != 0 {
1124 unsafe {
1125 shell_generic_error(c"Cannot save to ".as_ptr() as *const c_char, b"", b"");
1126 shell_generic_error(filename, b"", b"\n");
1127 }
1128 return -1;
1129 }
1130 let typ = unsafe { (*(*ctxt).doc).type_ };
1131 unsafe {
1132 match typ {
1133 t if t == xmlElementType::XML_DOCUMENT_NODE as c_int => {
1134 if xmlSaveFile(filename, (*ctxt).doc) < 0 {
1135 shell_generic_error(c"Failed to save to ".as_ptr() as *const c_char, b"", b"");
1136 shell_generic_error(filename, b"", b"\n");
1137 }
1138 }
1139 t if t == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int => {
1140 if shell_save_html_doc(filename, (*ctxt).doc) < 0 {
1141 shell_generic_error(c"Failed to save to ".as_ptr() as *const c_char, b"", b"");
1142 shell_generic_error(filename, b"", b"\n");
1143 }
1144 }
1145 _ => {
1146 shell_generic_error(
1147 c"To save to subparts of a document use the 'write' command\n".as_ptr()
1148 as *const c_char,
1149 b"",
1150 b"",
1151 );
1152 return -1;
1153 }
1154 }
1155 }
1156 0
1157}
1158
1159unsafe extern "C" fn shell_valid_error(_ctx: *mut c_void, msg: *const c_char) {
1166 unsafe {
1167 if !msg.is_null() {
1168 out_cstr(stderr, msg);
1169 }
1170 }
1171}
1172
1173unsafe fn shell_parse_dtd(dtd: *const c_char) -> *mut _xmlDtd {
1181 if dtd.is_null() {
1182 return ptr::null_mut();
1183 }
1184 let path = match unsafe { core::ffi::CStr::from_ptr(dtd) }.to_str() {
1185 Ok(p) => p,
1186 Err(_) => return ptr::null_mut(),
1187 };
1188 let content = match std::fs::read(path) {
1189 Ok(c) => c,
1190 Err(_) => return ptr::null_mut(),
1191 };
1192 let lower: Vec<u8> = content.iter().map(|b| b.to_ascii_lowercase()).collect();
1194 let pos = match find_subslice(&lower, b"<!doctype") {
1195 Some(p) => p,
1196 None => {
1199 let base = path.rsplit(['/', '\\']).next().unwrap_or(path);
1200 let name_c = bytes_to_xmlstr(base.as_bytes());
1201 let dtd_node =
1202 crate::xml::dtd::new_dtd(ptr::null_mut(), name_c, ptr::null_mut(), ptr::null_mut());
1203 if !name_c.is_null() {
1204 xmlFreeImpl(name_c as *mut c_void);
1205 }
1206 return dtd_node;
1207 }
1208 };
1209 let mut i = pos + b"<!doctype".len();
1210 while i < content.len() && (content[i] as char).is_ascii_whitespace() {
1212 i += 1;
1213 }
1214 let name_start = i;
1216 while i < content.len()
1217 && !(content[i] as char).is_ascii_whitespace()
1218 && content[i] != b'>'
1219 && content[i] != b'['
1220 {
1221 i += 1;
1222 }
1223 if i == name_start {
1224 return ptr::null_mut();
1225 }
1226 let name = &content[name_start..i];
1227
1228 while i < content.len() && (content[i] as char).is_ascii_whitespace() {
1230 i += 1;
1231 }
1232 let mut public_id: Option<&[u8]> = None;
1233 let mut system_id: Option<&[u8]> = None;
1234 if i < content.len() && content[i..].to_ascii_lowercase().starts_with(b"public") {
1235 i += b"public".len();
1236 while i < content.len() && (content[i] as char).is_ascii_whitespace() {
1237 i += 1;
1238 }
1239 if i < content.len() && content[i] == b'"' {
1240 let s = i + 1;
1241 let e = content[s..].iter().position(|&c| c == b'"').map(|p| s + p);
1242 if let Some(e) = e {
1243 public_id = Some(&content[s..e]);
1244 }
1245 }
1246 while i < content.len() && (content[i] as char).is_ascii_whitespace() {
1247 i += 1;
1248 }
1249 if i < content.len() && content[i] == b'"' {
1250 let s = i + 1;
1251 let e = content[s..].iter().position(|&c| c == b'"').map(|p| s + p);
1252 if let Some(e) = e {
1253 system_id = Some(&content[s..e]);
1254 }
1255 }
1256 } else if i < content.len() && content[i..].to_ascii_lowercase().starts_with(b"system") {
1257 i += b"system".len();
1258 while i < content.len() && (content[i] as char).is_ascii_whitespace() {
1259 i += 1;
1260 }
1261 if i < content.len() && content[i] == b'"' {
1262 let s = i + 1;
1263 let e = content[s..].iter().position(|&c| c == b'"').map(|p| s + p);
1264 if let Some(e) = e {
1265 system_id = Some(&content[s..e]);
1266 }
1267 }
1268 }
1269
1270 let name_c = bytes_to_xmlstr(name);
1271 let ext_c = match public_id {
1272 Some(v) => bytes_to_xmlstr(v),
1273 None => ptr::null_mut(),
1274 };
1275 let sys_c = match system_id {
1276 Some(v) => bytes_to_xmlstr(v),
1277 None => ptr::null_mut(),
1278 };
1279 let dtd_node = crate::xml::dtd::new_dtd(ptr::null_mut(), name_c, ext_c, sys_c);
1280 if !name_c.is_null() {
1281 xmlFreeImpl(name_c as *mut c_void);
1282 }
1283 if !ext_c.is_null() {
1284 xmlFreeImpl(ext_c as *mut c_void);
1285 }
1286 if !sys_c.is_null() {
1287 xmlFreeImpl(sys_c as *mut c_void);
1288 }
1289 dtd_node
1290}
1291
1292fn find_subslice(haystack: &[u8], needle: &[u8]) -> Option<usize> {
1294 if needle.is_empty() || haystack.len() < needle.len() {
1295 return None;
1296 }
1297 haystack.windows(needle.len()).position(|w| w == needle)
1298}
1299
1300unsafe fn bytes_to_xmlstr(bytes: &[u8]) -> *mut xmlChar {
1302 let buf = xmlMallocImpl(bytes.len() + 1) as *mut xmlChar;
1303 if buf.is_null() {
1304 return ptr::null_mut();
1305 }
1306 unsafe {
1307 ptr::copy_nonoverlapping(bytes.as_ptr(), buf, bytes.len());
1308 *buf.add(bytes.len()) = 0;
1309 }
1310 buf
1311}
1312
1313#[no_mangle]
1331pub unsafe extern "C" fn xmlShellValidate(
1332 ctxt: *mut _xmlShellCtxt,
1333 dtd: *mut c_char,
1334 _node: *mut _xmlNode,
1335 _node2: *mut _xmlNode,
1336) -> c_int {
1337 if ctxt.is_null() || unsafe { (*ctxt).doc }.is_null() {
1338 return -1;
1339 }
1340 let vctxt = xmlMallocZero(size_of::<_xmlValidCtxt>()) as *mut _xmlValidCtxt;
1342 if vctxt.is_null() {
1343 return -1;
1344 }
1345 unsafe {
1346 (*vctxt).error = Some(shell_valid_error);
1347 (*vctxt).warning = Some(shell_valid_error);
1348 }
1349 let mut res = -1;
1350 unsafe {
1351 if dtd.is_null() || *dtd == 0 {
1352 res = xmlValidateDocument(vctxt, (*ctxt).doc);
1353 } else {
1354 let subset = shell_parse_dtd(dtd as *const c_char);
1355 if !subset.is_null() {
1356 res = xmlValidateDtd(vctxt, (*ctxt).doc, subset);
1357 crate::xml::dtd::free_dtd(subset);
1358 }
1359 }
1360 }
1361 unsafe {
1362 xmlFreeImpl(vctxt as *mut c_void);
1363 }
1364 res
1365}
1366
1367#[no_mangle]
1390pub unsafe extern "C" fn xmlShellDu(
1391 ctxt: *mut _xmlShellCtxt,
1392 _arg: *mut c_char,
1393 tree: *mut _xmlNode,
1394 _node2: *mut _xmlNode,
1395) -> c_int {
1396 if ctxt.is_null() {
1397 return -1;
1398 }
1399 if tree.is_null() {
1400 return -1;
1401 }
1402 let out = unsafe { (*ctxt).output };
1403 let mut indent: c_int = 0;
1404 let mut node: *mut _xmlNode = tree;
1405 unsafe {
1406 while !node.is_null() {
1407 let typ = (*node).type_;
1408 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
1409 || typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
1410 {
1411 out_bytes(out, b"/\n");
1412 } else if typ == xmlElementType::XML_ELEMENT_NODE as c_int {
1413 let mut line = Vec::new();
1414 for _ in 0..indent {
1415 line.extend_from_slice(b" ");
1416 }
1417 if !(*node).ns.is_null() && !(*(*node).ns).prefix.is_null() {
1418 push_cstr(&mut line, (*(*node).ns).prefix as *const c_char);
1419 line.push(b':');
1420 }
1421 push_cstr(&mut line, (*node).name as *const c_char);
1422 line.push(b'\n');
1423 out_bytes(out, &line);
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 indent += 1;
1438 } else if node != tree && !(*node).next.is_null() {
1439 node = (*node).next;
1440 } else if node != tree {
1441 while node != tree {
1442 if !(*node).parent.is_null() {
1443 node = (*node).parent;
1444 indent -= 1;
1445 }
1446 if node != tree && !(*node).next.is_null() {
1447 node = (*node).next;
1448 break;
1449 }
1450 if (*node).parent.is_null() {
1451 node = ptr::null_mut();
1452 break;
1453 }
1454 if node == tree {
1455 node = ptr::null_mut();
1456 break;
1457 }
1458 }
1459 if node == tree {
1460 node = ptr::null_mut();
1461 }
1462 } else {
1463 node = ptr::null_mut();
1464 }
1465 }
1466 }
1467 0
1468}
1469
1470#[no_mangle]
1489pub unsafe extern "C" fn xmlShellPwd(
1490 _ctxt: *mut _xmlShellCtxt,
1491 buffer: *mut c_char,
1492 node: *mut _xmlNode,
1493 _node2: *mut _xmlNode,
1494) -> c_int {
1495 if node.is_null() || buffer.is_null() {
1496 return -1;
1497 }
1498
1499 let path = unsafe { shell_get_node_path(node) };
1500 if path.is_null() {
1501 return -1;
1502 }
1503
1504 let plen = unsafe { tree::xml_strlen(path) } as usize;
1506 let n = plen.min(498);
1507 unsafe {
1508 ptr::copy_nonoverlapping(path as *const u8, buffer as *mut u8, n);
1509 *buffer.add(n) = 0;
1510 *buffer.add(499) = b'0' as c_char;
1511 }
1512 unsafe {
1513 xmlFreeImpl(path as *mut c_void);
1514 }
1515 0
1516}
1517
1518unsafe fn xmlShellSetBase(ctxt: *mut _xmlShellCtxt, arg: *mut c_char, node: *mut _xmlNode) {
1524 let _ = ctxt;
1525 if !node.is_null() {
1526 unsafe {
1527 xmlNodeSetBase(node, arg as *const xmlChar);
1528 }
1529 }
1530}
1531
1532unsafe fn xmlShellRegisterNamespace(ctxt: *mut _xmlShellCtxt, arg: *mut c_char) -> c_int {
1535 let ns_list_dup = unsafe { xmlStrdup(arg as *const xmlChar) };
1536 if ns_list_dup.is_null() {
1537 return -1;
1538 }
1539 let mut next: *mut xmlChar = ns_list_dup;
1540 loop {
1541 if unsafe { *next == 0 } {
1542 break;
1543 }
1544 let prefix = next;
1546 let eq = unsafe { xmlStrchr(next, b'=' as xmlChar) };
1547 if eq.is_null() {
1548 unsafe {
1549 out_cstr(
1550 (*ctxt).output,
1551 c"setns: prefix=[nsuri] required\n".as_ptr() as *const c_char,
1552 );
1553 }
1554 unsafe {
1555 xmlFreeImpl(ns_list_dup as *mut c_void);
1556 }
1557 return -1;
1558 }
1559 unsafe {
1561 *(eq as *mut xmlChar) = 0;
1562 }
1563 let href = unsafe { eq.add(1) };
1564 let space = unsafe { xmlStrchr(href, b' ' as xmlChar) };
1566 if !space.is_null() {
1567 unsafe {
1568 *(space as *mut xmlChar) = 0;
1569 }
1570 next = unsafe { space.add(1) as *mut xmlChar };
1571 } else {
1572 next = unsafe { href.add(tree::xml_strlen(href) as usize) as *mut xmlChar };
1573 }
1574
1575 if unsafe { xmlXPathRegisterNs((*ctxt).pctxt, prefix, href) } != 0 {
1577 unsafe {
1578 let mut msg = Vec::new();
1579 msg.extend_from_slice(b"Error: unable to register NS with prefix=\"");
1580 push_cstr(&mut msg, prefix as *const c_char);
1581 msg.extend_from_slice(b"\" and href=\"");
1582 push_cstr(&mut msg, href as *const c_char);
1583 msg.extend_from_slice(b"\"\n");
1584 out_bytes((*ctxt).output, &msg);
1585 }
1586 unsafe {
1587 xmlFreeImpl(ns_list_dup as *mut c_void);
1588 }
1589 return -1;
1590 }
1591 }
1592 unsafe {
1593 xmlFreeImpl(ns_list_dup as *mut c_void);
1594 }
1595 0
1596}
1597
1598unsafe fn xmlShellRegisterRootNamespaces(ctxt: *mut _xmlShellCtxt, root: *mut _xmlNode) -> c_int {
1601 if root.is_null()
1602 || unsafe { (*root).type_ != xmlElementType::XML_ELEMENT_NODE as c_int }
1603 || unsafe { (*root).nsDef.is_null() }
1604 || ctxt.is_null()
1605 || unsafe { (*ctxt).pctxt.is_null() }
1606 {
1607 return -1;
1608 }
1609 let mut ns = unsafe { (*root).nsDef };
1610 while !ns.is_null() {
1611 if unsafe { (*ns).prefix.is_null() } {
1612 unsafe {
1613 xmlXPathRegisterNs(
1614 (*ctxt).pctxt,
1615 c"defaultns".as_ptr() as *const xmlChar,
1616 (*ns).href,
1617 );
1618 }
1619 } else {
1620 unsafe {
1621 xmlXPathRegisterNs((*ctxt).pctxt, (*ns).prefix, (*ns).href);
1622 }
1623 }
1624 ns = unsafe { (*ns).next };
1625 }
1626 0
1627}
1628
1629unsafe fn xmlShellGrep(ctxt: *mut _xmlShellCtxt, arg: *mut c_char, node: *mut _xmlNode) {
1632 if ctxt.is_null() || node.is_null() || arg.is_null() {
1633 return;
1634 }
1635 let mut node = node;
1636 while !node.is_null() {
1637 unsafe {
1638 let typ = (*node).type_;
1639 if typ == xmlElementType::XML_COMMENT_NODE as c_int {
1640 if !xmlStrstr((*node).content, arg as *const xmlChar).is_null() {
1641 let path = shell_get_node_path(node);
1642 if !path.is_null() {
1643 let mut line = Vec::new();
1644 push_cstr(&mut line, path as *const c_char);
1645 line.extend_from_slice(b" : ");
1646 out_bytes((*ctxt).output, &line);
1647 xmlFreeImpl(path as *mut c_void);
1648 }
1649 xmlShellList(ctxt, ptr::null_mut(), node, ptr::null_mut());
1650 }
1651 } else if typ == xmlElementType::XML_TEXT_NODE as c_int
1652 && !xmlStrstr((*node).content, arg as *const xmlChar).is_null()
1653 {
1654 let path = shell_get_node_path((*node).parent);
1655 if !path.is_null() {
1656 let mut line = Vec::new();
1657 push_cstr(&mut line, path as *const c_char);
1658 line.extend_from_slice(b" : ");
1659 out_bytes((*ctxt).output, &line);
1660 xmlFreeImpl(path as *mut c_void);
1661 }
1662 xmlShellList(ctxt, ptr::null_mut(), (*node).parent, ptr::null_mut());
1663 }
1664
1665 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
1669 || typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
1670 {
1671 node = (*(node as *mut _xmlDoc)).children;
1672 } else if !(*node).children.is_null()
1673 && typ != xmlElementType::XML_ENTITY_REF_NODE as c_int
1674 {
1675 node = (*node).children;
1676 } else if !(*node).next.is_null() {
1677 node = (*node).next;
1678 } else {
1679 while !node.is_null() {
1680 if !(*node).parent.is_null() {
1681 node = (*node).parent;
1682 }
1683 if !(*node).next.is_null() {
1684 node = (*node).next;
1685 break;
1686 }
1687 if (*node).parent.is_null() {
1688 node = ptr::null_mut();
1689 break;
1690 }
1691 }
1692 }
1693 }
1694 }
1695}
1696
1697unsafe fn shell_result_type_error(arg: *const c_char, typ: c_int) {
1700 if typ == xmlXPathObjectType::XPATH_UNDEFINED as c_int {
1701 unsafe { shell_generic_error(arg, b": ", b"no such node\n") };
1702 } else if typ == xmlXPathObjectType::XPATH_BOOLEAN as c_int {
1703 unsafe { shell_generic_error(arg, b" is a Boolean", b"\n") };
1704 } else if typ == xmlXPathObjectType::XPATH_NUMBER as c_int {
1705 unsafe { shell_generic_error(arg, b" is a number", b"\n") };
1706 } else if typ == xmlXPathObjectType::XPATH_STRING as c_int {
1707 unsafe { shell_generic_error(arg, b" is a string", b"\n") };
1708 } else if typ == xmlXPathObjectType::XPATH_POINT as c_int {
1709 unsafe { shell_generic_error(arg, b" is a point", b"\n") };
1710 } else if typ == xmlXPathObjectType::XPATH_RANGE as c_int
1711 || typ == xmlXPathObjectType::XPATH_LOCATIONSET as c_int
1712 {
1713 unsafe { shell_generic_error(arg, b" is a range", b"\n") };
1714 } else if typ == xmlXPathObjectType::XPATH_USERS as c_int {
1715 unsafe { shell_generic_error(arg, b" is user-defined", b"\n") };
1716 } else if typ == xmlXPathObjectType::XPATH_XSLT_TREE as c_int {
1717 unsafe { shell_generic_error(arg, b" is an XSLT value tree", b"\n") };
1718 }
1719}
1720
1721unsafe fn shell_build_prompt(ctxt: *mut _xmlShellCtxt) -> Vec<u8> {
1728 let mut p = Vec::new();
1729 let node = unsafe { (*ctxt).node };
1730 let doc = unsafe { (*ctxt).doc };
1731 if node == doc as *mut _xmlNode {
1732 p.extend_from_slice(b"/ > ");
1733 } else if !node.is_null() && !unsafe { (*node).name }.is_null() {
1734 unsafe {
1735 if !(*node).ns.is_null() && !(*(*node).ns).prefix.is_null() {
1736 push_cstr(&mut p, (*(*node).ns).prefix as *const c_char);
1737 p.push(b':');
1738 }
1739 push_cstr(&mut p, (*node).name as *const c_char);
1740 }
1741 p.extend_from_slice(b" > ");
1742 } else {
1743 p.extend_from_slice(b"? > ");
1744 }
1745 p.push(0);
1746 p
1747}
1748
1749unsafe fn shell_print_help(ctxt: *mut _xmlShellCtxt) {
1751 let out = unsafe { (*ctxt).output };
1752 const HELP: &[&[u8]] = &[
1753 b"\tbase display XML base of the node\n",
1754 b"\tsetbase URI change the XML base of the node\n",
1755 b"\tbye leave shell\n",
1756 b"\tcat [node] display node or current node\n",
1757 b"\tcd [path] change directory to path or to root\n",
1758 b"\tdir [path] dumps information about the node (namespace, attributes, content)\n",
1759 b"\tdu [path] show the structure of the subtree under path or the current node\n",
1760 b"\texit leave shell\n",
1761 b"\thelp display this help\n",
1762 b"\tfree display memory usage\n",
1763 b"\tload [name] load a new document with name\n",
1764 b"\tls [path] list contents of path or the current directory\n",
1765 b"\txpath expr evaluate the XPath expression in that context and print the result\n",
1766 b"\tsetns nsreg register a namespace to a prefix in the XPath evaluation context\n",
1767 b"\t format for nsreg is: prefix=[nsuri] (i.e. prefix= unsets a prefix)\n",
1768 b"\tsetrootns register all namespace found on the root element\n",
1769 b"\t the default namespace if any uses 'defaultns' prefix\n",
1770 b"\tpwd display current working directory\n",
1771 b"\twhereis display absolute path of [path] or current working directory\n",
1772 b"\tquit leave shell\n",
1773 b"\tsave [name] save this document to name or the original name\n",
1774 b"\twrite [name] write the current node to the filename\n",
1775 b"\tvalidate check the document for errors\n",
1776 b"\tgrep string search for a string in the subtree\n",
1777 ];
1778 for line in HELP {
1779 unsafe {
1780 out_bytes(out, line);
1781 }
1782 }
1783}
1784
1785#[no_mangle]
1804pub unsafe extern "C" fn xmlShell(
1805 doc: *mut _xmlDoc,
1806 filename: *mut c_char,
1807 input: xmlShellReadlineFunc,
1808 output: *mut c_void,
1809) {
1810 if doc.is_null() || filename.is_null() || input.is_none() {
1811 return;
1812 }
1813 let output = if output.is_null() {
1814 unsafe { stdout }
1815 } else {
1816 output
1817 };
1818
1819 let ctxt = xmlMallocZero(size_of::<_xmlShellCtxt>()) as *mut _xmlShellCtxt;
1820 if ctxt.is_null() {
1821 return;
1822 }
1823 unsafe {
1824 (*ctxt).loaded = 0;
1825 (*ctxt).doc = doc;
1826 (*ctxt).input = input;
1827 (*ctxt).output = output;
1828 (*ctxt).filename = xmlStrdup(filename as *const xmlChar) as *mut c_char;
1829 (*ctxt).node = doc as *mut _xmlNode;
1830 (*ctxt).pctxt = xmlXPathNewContext(doc);
1831 }
1832 if unsafe { (*ctxt).pctxt }.is_null() {
1833 unsafe {
1834 xmlFreeImpl(ctxt as *mut c_void);
1835 }
1836 return;
1837 }
1838
1839 let mut cmdline: *mut c_char = ptr::null_mut();
1840 loop {
1841 let prompt = unsafe { shell_build_prompt(ctxt) };
1843 let readline = unsafe { (*ctxt).input };
1844 cmdline = match readline {
1845 Some(f) => f(prompt.as_ptr() as *mut c_char),
1846 None => break,
1847 };
1848 if cmdline.is_null() {
1849 break;
1850 }
1851
1852 let clen = unsafe { libc::strlen(cmdline) } as usize;
1854 let cbytes = unsafe { core::slice::from_raw_parts(cmdline as *const u8, clen) };
1855 let mut i = 0usize;
1856 while i < clen && (cbytes[i] == b' ' || cbytes[i] == b'\t') {
1857 i += 1;
1858 }
1859 let mut command: Vec<u8> = Vec::new();
1860 while i < clen
1861 && cbytes[i] != b' '
1862 && cbytes[i] != b'\t'
1863 && cbytes[i] != b'\n'
1864 && cbytes[i] != b'\r'
1865 {
1866 command.push(cbytes[i]);
1867 i += 1;
1868 }
1869 if command.is_empty() {
1870 unsafe {
1871 libc::free(cmdline as *mut c_void);
1872 }
1873 cmdline = ptr::null_mut();
1874 continue;
1875 }
1876
1877 while i < clen && (cbytes[i] == b' ' || cbytes[i] == b'\t') {
1879 i += 1;
1880 }
1881 let mut arg: Vec<u8> = Vec::new();
1882 while i < clen && cbytes[i] != b'\n' && cbytes[i] != b'\r' {
1883 arg.push(cbytes[i]);
1884 i += 1;
1885 }
1886
1887 command.push(0);
1889 let cmd: &[u8] = &command;
1890 let mut argn = arg.clone();
1891 argn.push(0);
1892 let arg_cstr: *mut c_char = argn.as_mut_ptr() as *mut c_char;
1893 let arg_xml: *const xmlChar = argn.as_ptr() as *const xmlChar;
1894
1895 if cmd == b"exit\0" || cmd == b"quit\0" || cmd == b"bye\0" {
1897 break;
1898 }
1899 if cmd == b"help\0" {
1900 unsafe { shell_print_help(ctxt) };
1901 } else if cmd == b"validate\0" {
1902 unsafe {
1903 xmlShellValidate(ctxt, arg_cstr, ptr::null_mut(), ptr::null_mut());
1904 }
1905 } else if cmd == b"load\0" {
1906 unsafe {
1907 xmlShellLoad(ctxt, arg_cstr, ptr::null_mut(), ptr::null_mut());
1908 }
1909 } else if cmd == b"save\0" {
1910 unsafe {
1911 xmlShellSave(ctxt, arg_cstr, ptr::null_mut(), ptr::null_mut());
1912 }
1913 } else if cmd == b"write\0" {
1914 if arg.is_empty() {
1915 unsafe {
1916 shell_generic_error(
1917 c"Write command requires a filename argument\n".as_ptr() as *const c_char,
1918 b"",
1919 b"",
1920 );
1921 }
1922 } else {
1923 unsafe {
1924 xmlShellWrite(ctxt, arg_cstr, (*ctxt).node, ptr::null_mut());
1925 }
1926 }
1927 } else if cmd == b"grep\0" {
1928 unsafe {
1929 xmlShellGrep(ctxt, arg_cstr, (*ctxt).node);
1930 }
1931 } else if cmd == b"free\0" {
1932 unsafe {
1933 if arg.is_empty() {
1934 crate::abi::allocator::xmlMemShow((*ctxt).output, 0);
1935 } else {
1936 let mut len: c_int = 0;
1937 let arg_s = core::str::from_utf8(&argn[..argn.len() - 1]).unwrap_or("");
1938 if let Ok(v) = arg_s.trim().parse::<c_int>() {
1939 len = v;
1940 }
1941 crate::abi::allocator::xmlMemShow((*ctxt).output, len);
1942 }
1943 }
1944 } else if cmd == b"pwd\0" {
1945 let mut dir = [0 as c_char; 500];
1946 unsafe {
1947 if xmlShellPwd(ctxt, dir.as_mut_ptr(), (*ctxt).node, ptr::null_mut()) == 0 {
1948 let mut line = Vec::new();
1949 push_cstr(&mut line, dir.as_mut_ptr());
1950 line.extend_from_slice(b"\n");
1951 out_bytes((*ctxt).output, &line);
1952 }
1953 }
1954 } else if cmd == b"du\0" {
1955 unsafe {
1956 if arg.is_empty() {
1957 xmlShellDu(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
1958 } else {
1959 (*(*ctxt).pctxt).node = (*ctxt).node;
1960 let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
1961 if !list.is_null() {
1962 let typ = (*list).type_;
1963 if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
1964 let ns = (*list).nodesetval as *mut _xmlNodeSet;
1965 if !ns.is_null() {
1966 for indx in 0..(*ns).nodeNr {
1967 let n = *(*ns).nodeTab.add(indx as usize);
1968 xmlShellDu(ctxt, ptr::null_mut(), n, ptr::null_mut());
1969 }
1970 }
1971 } else {
1972 shell_result_type_error(arg_cstr, typ);
1973 }
1974 xmlXPathFreeObject(list);
1975 } else {
1976 shell_generic_error(arg_cstr, b": ", b"no such node\n");
1977 }
1978 (*(*ctxt).pctxt).node = ptr::null_mut();
1979 }
1980 }
1981 } else if cmd == b"base\0" {
1982 unsafe {
1983 xmlShellBase(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
1984 }
1985 } else if cmd == b"setns\0" {
1986 unsafe {
1987 if arg.is_empty() {
1988 shell_generic_error(
1989 c"setns: prefix=[nsuri] required\n".as_ptr() as *const c_char,
1990 b"",
1991 b"",
1992 );
1993 } else {
1994 xmlShellRegisterNamespace(ctxt, arg_cstr);
1995 }
1996 }
1997 } else if cmd == b"setrootns\0" {
1998 unsafe {
1999 let root = tree::doc_get_root_element((*ctxt).doc);
2000 xmlShellRegisterRootNamespaces(ctxt, root);
2001 }
2002 } else if cmd == b"xpath\0" {
2003 unsafe {
2004 if arg.is_empty() {
2005 shell_generic_error(
2006 c"xpath: expression required\n".as_ptr() as *const c_char,
2007 b"",
2008 b"",
2009 );
2010 } else {
2011 (*(*ctxt).pctxt).node = (*ctxt).node;
2012 let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
2013 xmlXPathDebugDumpObject((*ctxt).output, list, 0);
2014 xmlXPathFreeObject(list);
2015 }
2016 }
2017 } else if cmd == b"setbase\0" {
2018 unsafe {
2019 xmlShellSetBase(ctxt, arg_cstr, (*ctxt).node);
2020 }
2021 } else if cmd == b"ls\0" || cmd == b"dir\0" {
2022 let is_dir = cmd == b"dir\0";
2023 unsafe {
2024 if arg.is_empty() {
2025 if is_dir {
2026 xmlShellDir(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
2027 } else {
2028 xmlShellList(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
2029 }
2030 } else {
2031 (*(*ctxt).pctxt).node = (*ctxt).node;
2032 let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
2033 if !list.is_null() {
2034 let typ = (*list).type_;
2035 if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
2036 let ns = (*list).nodesetval as *mut _xmlNodeSet;
2037 if !ns.is_null() {
2038 for indx in 0..(*ns).nodeNr {
2039 let n = *(*ns).nodeTab.add(indx as usize);
2040 if is_dir {
2041 xmlShellDir(ctxt, ptr::null_mut(), n, ptr::null_mut());
2042 } else {
2043 xmlShellList(ctxt, ptr::null_mut(), n, ptr::null_mut());
2044 }
2045 }
2046 }
2047 } else {
2048 shell_result_type_error(arg_cstr, typ);
2049 }
2050 xmlXPathFreeObject(list);
2051 } else {
2052 shell_generic_error(arg_cstr, b": ", b"no such node\n");
2053 }
2054 (*(*ctxt).pctxt).node = ptr::null_mut();
2055 }
2056 }
2057 } else if cmd == b"whereis\0" {
2058 let mut dir = [0 as c_char; 500];
2059 unsafe {
2060 if arg.is_empty() {
2061 if xmlShellPwd(ctxt, dir.as_mut_ptr(), (*ctxt).node, ptr::null_mut()) == 0 {
2062 let mut line = Vec::new();
2063 push_cstr(&mut line, dir.as_mut_ptr());
2064 line.extend_from_slice(b"\n");
2065 out_bytes((*ctxt).output, &line);
2066 }
2067 } else {
2068 (*(*ctxt).pctxt).node = (*ctxt).node;
2069 let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
2070 if !list.is_null() {
2071 let typ = (*list).type_;
2072 if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
2073 let ns = (*list).nodesetval as *mut _xmlNodeSet;
2074 if !ns.is_null() {
2075 for indx in 0..(*ns).nodeNr {
2076 let n = *(*ns).nodeTab.add(indx as usize);
2077 if xmlShellPwd(ctxt, dir.as_mut_ptr(), n, ptr::null_mut()) == 0
2078 {
2079 let mut line = Vec::new();
2080 push_cstr(&mut line, dir.as_mut_ptr());
2081 line.extend_from_slice(b"\n");
2082 out_bytes((*ctxt).output, &line);
2083 }
2084 }
2085 }
2086 } else {
2087 shell_result_type_error(arg_cstr, typ);
2088 }
2089 xmlXPathFreeObject(list);
2090 } else {
2091 shell_generic_error(arg_cstr, b": ", b"no such node\n");
2092 }
2093 (*(*ctxt).pctxt).node = ptr::null_mut();
2094 }
2095 }
2096 } else if cmd == b"cd\0" {
2097 unsafe {
2098 if arg.is_empty() {
2099 (*ctxt).node = (*ctxt).doc as *mut _xmlNode;
2100 } else {
2101 let mut argn = argn;
2103 let l = argn.len();
2104 if l >= 3 && argn[l - 2] == b'/' {
2105 argn[l - 2] = 0;
2106 }
2107 (*(*ctxt).pctxt).node = (*ctxt).node;
2108 let list = xmlXPathEval(argn.as_ptr() as *const xmlChar, (*ctxt).pctxt);
2109 if !list.is_null() {
2110 let typ = (*list).type_;
2111 if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
2112 let ns = (*list).nodesetval as *mut _xmlNodeSet;
2113 if !ns.is_null() {
2114 if (*ns).nodeNr == 1 {
2115 (*ctxt).node = *(*ns).nodeTab;
2116 if !(*ctxt).node.is_null()
2117 && (*(*ctxt).node).type_
2118 == xmlElementType::XML_NAMESPACE_DECL as c_int
2119 {
2120 shell_generic_error(
2121 c"cannot cd to namespace\n".as_ptr() as *const c_char,
2122 b"",
2123 b"",
2124 );
2125 (*ctxt).node = ptr::null_mut();
2126 }
2127 } else {
2128 let mut msg = Vec::new();
2129 push_cstr(&mut msg, arg_cstr);
2130 msg.extend_from_slice(b" is a ");
2131 msg.extend_from_slice((*ns).nodeNr.to_string().as_bytes());
2132 msg.extend_from_slice(b" Node Set\n");
2133 out_bytes(stderr, &msg);
2134 }
2135 } else {
2136 let mut msg = Vec::new();
2137 push_cstr(&mut msg, arg_cstr);
2138 msg.extend_from_slice(b" is an empty Node Set\n");
2139 out_bytes(stderr, &msg);
2140 }
2141 } else {
2142 shell_result_type_error(arg_cstr, typ);
2143 }
2144 xmlXPathFreeObject(list);
2145 } else {
2146 shell_generic_error(arg_cstr, b": ", b"no such node\n");
2147 }
2148 (*(*ctxt).pctxt).node = ptr::null_mut();
2149 }
2150 }
2151 } else if cmd == b"cat\0" {
2152 unsafe {
2153 if arg.is_empty() {
2154 xmlShellCat(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
2155 } else {
2156 (*(*ctxt).pctxt).node = (*ctxt).node;
2161 let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
2162 if !list.is_null() {
2163 let typ = (*list).type_;
2164 if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
2165 let ns = (*list).nodesetval as *mut _xmlNodeSet;
2166 if !ns.is_null() {
2167 for indx in 0..(*ns).nodeNr {
2168 if i > 0 {
2169 out_bytes((*ctxt).output, b" -------\n");
2170 }
2171 let n = *(*ns).nodeTab.add(indx as usize);
2172 xmlShellCat(ctxt, ptr::null_mut(), n, ptr::null_mut());
2173 }
2174 }
2175 } else {
2176 shell_result_type_error(arg_cstr, typ);
2177 }
2178 xmlXPathFreeObject(list);
2179 } else {
2180 shell_generic_error(arg_cstr, b": ", b"no such node\n");
2181 }
2182 (*(*ctxt).pctxt).node = ptr::null_mut();
2183 }
2184 }
2185 } else {
2186 let mut msg = Vec::new();
2187 msg.extend_from_slice(b"Unknown command ");
2188 msg.extend_from_slice(&command[..command.len() - 1]);
2189 msg.extend_from_slice(b"\n");
2190 unsafe {
2191 out_bytes(stderr, &msg);
2192 }
2193 }
2194
2195 unsafe {
2196 libc::free(cmdline as *mut c_void);
2197 }
2198 cmdline = ptr::null_mut();
2199 }
2200
2201 unsafe {
2203 xmlXPathFreeContext((*ctxt).pctxt);
2204 if (*ctxt).loaded != 0 {
2205 tree::free_doc((*ctxt).doc);
2206 }
2207 if !(*ctxt).filename.is_null() {
2208 xmlFreeImpl((*ctxt).filename as *mut c_void);
2209 }
2210 xmlFreeImpl(ctxt as *mut c_void);
2211 if !cmdline.is_null() {
2212 libc::free(cmdline as *mut c_void);
2213 }
2214 }
2215}