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;
113use core::ptr;
114use std::os::raw::{c_char, c_int};
115
116use crate::abi::allocator::{xmlFreeImpl, xmlMallocImpl, xmlMallocZero};
117use crate::abi::exports_string::xmlStrstr;
118use crate::abi::exports_tree::{xmlNodeGetBase, xmlNodeSetBase};
119use crate::abi::exports_uri::xmlCanonicPath;
120use crate::abi::exports_xml2::{
121 xmlReadFile, xmlSaveFile, xmlStrEqual, xmlStrchr, xmlStrdup, xmlValidateDocument,
122 xmlValidateDtd, xmlXPathEval, xmlXPathFreeContext, xmlXPathFreeObject, xmlXPathNewContext,
123 xmlXPathRegisterNs,
124};
125use crate::abi::structs::{
126 _xmlAttr, _xmlDoc, _xmlDtd, _xmlNode, _xmlNodeSet, _xmlValidCtxt, _xmlXPathContext,
127 _xmlXPathObject,
128};
129use crate::abi::types::{xmlChar, xmlElementType, xmlXPathObjectType};
130use crate::xml::xpath::exports::xmlXPathDebugDumpObject;
131use crate::xml::{debug, io, tree};
132
133pub type xmlShellReadlineFunc = Option<unsafe extern "C" fn(prompt: *mut c_char) -> *mut c_char>;
140
141#[repr(C)]
143pub struct _xmlShellCtxt {
144 pub filename: *mut c_char,
146 pub doc: *mut _xmlDoc,
148 pub node: *mut _xmlNode,
150 pub pctxt: *mut _xmlXPathContext,
152 pub loaded: c_int,
154 pub output: *mut c_void,
156 pub input: xmlShellReadlineFunc,
158}
159
160pub type xmlShellCmd = Option<
163 unsafe extern "C" fn(*mut _xmlShellCtxt, *mut c_char, *mut _xmlNode, *mut _xmlNode) -> c_int,
164>;
165
166extern "C" {
167 fn fwrite(ptr: *const c_void, size: usize, nmemb: usize, stream: *mut c_void) -> usize;
169 fn fputs(s: *const c_char, stream: *mut c_void) -> c_int;
171 static mut stdout: *mut c_void;
173 static mut stderr: *mut c_void;
175}
176
177unsafe fn push_cstr(v: &mut Vec<u8>, s: *const c_char) {
183 if s.is_null() {
184 return;
185 }
186 let len = libc::strlen(s);
187 v.extend_from_slice(core::slice::from_raw_parts(s as *const u8, len));
188}
189
190unsafe fn out_bytes(fp: *mut c_void, bytes: &[u8]) {
192 if fp.is_null() || bytes.is_empty() {
193 return;
194 }
195 unsafe {
196 fwrite(bytes.as_ptr() as *const c_void, 1, bytes.len(), fp);
197 }
198}
199
200unsafe fn out_cstr(fp: *mut c_void, s: *const c_char) {
202 if fp.is_null() || s.is_null() {
203 return;
204 }
205 unsafe {
206 fputs(s, fp);
207 }
208}
209
210unsafe fn shell_generic_error(arg: *const c_char, mid: &[u8], end: &[u8]) {
216 let mut v = Vec::new();
217 unsafe {
218 push_cstr(&mut v, arg);
219 }
220 v.extend_from_slice(mid);
221 v.extend_from_slice(end);
222 unsafe {
223 out_bytes(stderr, &v);
224 }
225}
226
227unsafe fn shell_elem_dump(fp: *mut c_void, doc: *mut _xmlDoc, node: *mut _xmlNode) -> c_int {
231 if fp.is_null() || node.is_null() {
232 return -1;
233 }
234 let buf = io::buf_create(-1);
235 if buf.is_null() {
236 return -1;
237 }
238 let ret = tree::node_dump(buf, doc, node, 0, 0);
239 if ret < 0 {
240 io::buf_free(buf);
241 return -1;
242 }
243 let content = io::buf_content(buf);
244 let len = io::buf_length(buf);
245 if !content.is_null() && len > 0 {
246 unsafe {
247 fwrite(content as *const c_void, 1, len as usize, fp);
248 }
249 }
250 io::buf_free(buf);
251 0
252}
253
254unsafe fn shell_html_doc_dump(fp: *mut c_void, doc: *mut _xmlDoc) -> c_int {
256 if fp.is_null() || doc.is_null() {
257 return -1;
258 }
259 let buf = io::buf_create(-1);
260 if buf.is_null() {
261 return -1;
262 }
263 let ret = crate::xml::html::doc_dump(buf, doc);
264 if ret < 0 {
265 io::buf_free(buf);
266 return -1;
267 }
268 let content = io::buf_content(buf);
269 let len = io::buf_length(buf);
270 if !content.is_null() && len > 0 {
271 unsafe {
272 fwrite(content as *const c_void, 1, len as usize, fp);
273 }
274 }
275 io::buf_free(buf);
276 ret
277}
278
279unsafe fn shell_html_node_dump_file(fp: *mut c_void, node: *mut _xmlNode) -> c_int {
281 if fp.is_null() || node.is_null() {
282 return -1;
283 }
284 let buf = io::buf_create(-1);
285 if buf.is_null() {
286 return -1;
287 }
288 let before = io::buf_length(buf);
289 crate::xml::html::serialize_node(node, buf, 0, 0);
290 let after = io::buf_length(buf);
291 if after < 0 || before < 0 {
292 io::buf_free(buf);
293 return -1;
294 }
295 let content = io::buf_content(buf);
296 let len = io::buf_length(buf);
297 if !content.is_null() && len > 0 {
298 unsafe {
299 fwrite(content as *const c_void, 1, len as usize, fp);
300 }
301 }
302 io::buf_free(buf);
303 after - before
304}
305
306unsafe fn shell_get_node_path(node: *const _xmlNode) -> *mut xmlChar {
309 if node.is_null() || (*node).type_ == xmlElementType::XML_NAMESPACE_DECL as c_int {
310 return ptr::null_mut();
311 }
312
313 let mut segments: Vec<Vec<u8>> = Vec::new();
316 let mut cur: *const _xmlNode = node;
317
318 loop {
319 if cur.is_null() {
320 break;
321 }
322 let typ = (*cur).type_;
323 let mut seg: Vec<u8> = Vec::new();
324 let mut occur: c_int = 0;
325 let mut generic: bool;
326 let next: *const _xmlNode;
327
328 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
329 || typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
330 {
331 if !segments.is_empty() {
334 break;
335 }
336 seg.extend_from_slice(b"/");
337 next = ptr::null();
338 } else if typ == xmlElementType::XML_ELEMENT_NODE as c_int {
339 generic = false;
340 seg.extend_from_slice(b"/");
341 let name = (*cur).name;
342 let ns = (*cur).ns;
343 if !name.is_null() {
344 if !ns.is_null() && !(*ns).prefix.is_null() {
345 unsafe {
346 push_cstr(&mut seg, (*ns).prefix as *const c_char);
347 }
348 seg.push(b':');
349 unsafe {
350 push_cstr(&mut seg, name as *const c_char);
351 }
352 } else if !ns.is_null() {
353 generic = true;
355 seg.extend_from_slice(b"*");
356 } else {
357 unsafe {
358 push_cstr(&mut seg, name as *const c_char);
359 }
360 }
361 }
362 next = (*cur).parent;
363
364 let mut tmp = (*cur).prev;
366 while !tmp.is_null() {
367 if (*tmp).type_ == xmlElementType::XML_ELEMENT_NODE as c_int
368 && (generic || unsafe { shell_same_element_name(cur, tmp) })
369 {
370 occur += 1;
371 }
372 tmp = (*tmp).prev;
373 }
374 if occur == 0 {
375 let mut tmp = (*cur).next;
376 while !tmp.is_null() && occur == 0 {
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).next;
383 }
384 if occur != 0 {
385 occur = 1;
386 }
387 } else {
388 occur += 1;
389 }
390 } else if typ == xmlElementType::XML_COMMENT_NODE as c_int {
391 seg.extend_from_slice(b"/comment()");
392 next = (*cur).parent;
393
394 let mut tmp = (*cur).prev;
395 while !tmp.is_null() {
396 if (*tmp).type_ == xmlElementType::XML_COMMENT_NODE as c_int {
397 occur += 1;
398 }
399 tmp = (*tmp).prev;
400 }
401 if occur == 0 {
402 let mut tmp = (*cur).next;
403 while !tmp.is_null() && occur == 0 {
404 if (*tmp).type_ == xmlElementType::XML_COMMENT_NODE as c_int {
405 occur += 1;
406 }
407 tmp = (*tmp).next;
408 }
409 if occur != 0 {
410 occur = 1;
411 }
412 } else {
413 occur += 1;
414 }
415 } else if typ == xmlElementType::XML_TEXT_NODE as c_int
416 || typ == xmlElementType::XML_CDATA_SECTION_NODE as c_int
417 {
418 seg.extend_from_slice(b"/text()");
419 next = (*cur).parent;
420
421 let mut tmp = (*cur).prev;
422 while !tmp.is_null() {
423 if (*tmp).type_ == xmlElementType::XML_TEXT_NODE as c_int
424 || (*tmp).type_ == xmlElementType::XML_CDATA_SECTION_NODE as c_int
425 {
426 occur += 1;
427 }
428 tmp = (*tmp).prev;
429 }
430 if occur == 0 {
431 let mut tmp = (*cur).next;
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 break;
438 }
439 tmp = (*tmp).next;
440 }
441 } else {
442 occur += 1;
443 }
444 } else if typ == xmlElementType::XML_PI_NODE as c_int {
445 let mut nm = Vec::new();
446 nm.extend_from_slice(b"processing-instruction('");
447 unsafe {
448 push_cstr(&mut nm, (*cur).name as *const c_char);
449 }
450 nm.extend_from_slice(b"')");
451 seg.extend_from_slice(b"/");
452 seg.extend_from_slice(&nm);
453 next = (*cur).parent;
454
455 let mut tmp = (*cur).prev;
456 while !tmp.is_null() {
457 if (*tmp).type_ == xmlElementType::XML_PI_NODE as c_int
458 && unsafe { xmlStrEqual((*cur).name, (*tmp).name) != 0 }
459 {
460 occur += 1;
461 }
462 tmp = (*tmp).prev;
463 }
464 if occur == 0 {
465 let mut tmp = (*cur).next;
466 while !tmp.is_null() && occur == 0 {
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).next;
473 }
474 if occur != 0 {
475 occur = 1;
476 }
477 } else {
478 occur += 1;
479 }
480 } else if typ == xmlElementType::XML_ATTRIBUTE_NODE as c_int {
481 seg.extend_from_slice(b"/@");
482 let attr = cur as *const _xmlAttr;
483 let name = (*attr).name;
484 if !name.is_null() {
485 if !(*attr).ns.is_null() && !(*(*attr).ns).prefix.is_null() {
486 unsafe {
487 push_cstr(&mut seg, (*(*attr).ns).prefix as *const c_char);
488 }
489 seg.push(b':');
490 }
491 unsafe {
492 push_cstr(&mut seg, name as *const c_char);
493 }
494 }
495 next = (*attr).parent;
496 } else {
497 return ptr::null_mut();
498 }
499
500 if occur != 0 {
501 seg.extend_from_slice(format!("[{}]", occur).as_bytes());
502 }
503 segments.push(seg);
504 if next.is_null() {
505 break;
506 }
507 cur = next;
508 }
509
510 let mut path: Vec<u8> = Vec::new();
512 for seg in segments.iter().rev() {
513 path.extend_from_slice(seg);
514 }
515 path.push(0);
516
517 let ret = xmlMallocImpl(path.len()) as *mut xmlChar;
518 if ret.is_null() {
519 return ptr::null_mut();
520 }
521 unsafe {
522 ptr::copy_nonoverlapping(path.as_ptr(), ret, path.len());
523 }
524 ret
525}
526
527unsafe fn shell_same_element_name(a: *const _xmlNode, b: *const _xmlNode) -> bool {
530 unsafe {
531 if xmlStrEqual((*a).name, (*b).name) == 0 {
532 return false;
533 }
534 let ans = (*a).ns;
535 let bns = (*b).ns;
536 if ans == bns {
537 return true;
538 }
539 if !ans.is_null() && !bns.is_null() {
540 return xmlStrEqual((*ans).prefix, (*bns).prefix) != 0;
541 }
542 false
543 }
544}
545
546#[no_mangle]
569pub unsafe extern "C" fn xmlShellPrintXPathError(errorType: c_int, arg: *const c_char) {
570 let default_arg = b"Result\0";
571 let arg = if arg.is_null() {
572 default_arg.as_ptr() as *const c_char
573 } else {
574 arg
575 };
576
577 if errorType == xmlXPathObjectType::XPATH_UNDEFINED as c_int {
578 unsafe { shell_generic_error(arg, b": ", b"no such node\n") };
579 } else if errorType == xmlXPathObjectType::XPATH_BOOLEAN as c_int {
580 unsafe { shell_generic_error(arg, b" is a Boolean", b"\n") };
581 } else if errorType == xmlXPathObjectType::XPATH_NUMBER as c_int {
582 unsafe { shell_generic_error(arg, b" is a number", b"\n") };
583 } else if errorType == xmlXPathObjectType::XPATH_STRING as c_int {
584 unsafe { shell_generic_error(arg, b" is a string", b"\n") };
585 } else if errorType == xmlXPathObjectType::XPATH_POINT as c_int {
586 unsafe { shell_generic_error(arg, b" is a point", b"\n") };
587 } else if errorType == xmlXPathObjectType::XPATH_RANGE as c_int
588 || errorType == xmlXPathObjectType::XPATH_LOCATIONSET as c_int
589 {
590 unsafe { shell_generic_error(arg, b" is a range", b"\n") };
591 } else if errorType == xmlXPathObjectType::XPATH_USERS as c_int {
592 unsafe { shell_generic_error(arg, b" is user-defined", b"\n") };
593 } else if errorType == xmlXPathObjectType::XPATH_XSLT_TREE as c_int {
594 unsafe { shell_generic_error(arg, b" is an XSLT value tree", b"\n") };
595 }
596}
597
598unsafe fn xmlShellPrintNodeCtxt(ctxt: *mut _xmlShellCtxt, node: *mut _xmlNode) {
601 if node.is_null() {
602 return;
603 }
604 let fp = if ctxt.is_null() {
605 unsafe { stdout }
606 } else {
607 (*ctxt).output
608 };
609
610 let typ = unsafe { (*node).type_ };
611 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int {
612 tree::xmlDocDump(fp, node as *mut _xmlDoc);
613 } else if typ == xmlElementType::XML_ATTRIBUTE_NODE as c_int {
614 unsafe {
615 debug::xmlDebugDumpAttrList(fp as *mut debug::_IO_FILE, node as *mut _xmlAttr, 0);
616 }
617 } else {
618 unsafe {
619 shell_elem_dump(fp, (*node).doc, node);
620 }
621 }
622 unsafe {
623 out_bytes(fp, b"\n");
624 }
625}
626
627#[no_mangle]
644pub unsafe extern "C" fn xmlShellPrintNode(node: *mut _xmlNode) {
645 unsafe { xmlShellPrintNodeCtxt(ptr::null_mut(), node) };
646}
647
648#[no_mangle]
668pub unsafe extern "C" fn xmlShellPrintXPathResult(list: *mut _xmlXPathObject) {
669 unsafe {
670 xmlXPathDebugDumpObject(stdout, list, 0);
671 }
672}
673
674#[no_mangle]
696pub unsafe extern "C" fn xmlShellList(
697 ctxt: *mut _xmlShellCtxt,
698 _arg: *mut c_char,
699 node: *mut _xmlNode,
700 _node2: *mut _xmlNode,
701) -> c_int {
702 if ctxt.is_null() {
703 return 0;
704 }
705 if node.is_null() {
706 unsafe {
707 out_bytes((*ctxt).output, b"NULL\n");
708 }
709 return 0;
710 }
711 let typ = unsafe { (*node).type_ };
712 let mut cur: *mut _xmlNode;
713 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
714 || typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
715 {
716 cur = unsafe { (*(node as *mut _xmlDoc)).children };
717 } else if typ == xmlElementType::XML_NAMESPACE_DECL as c_int {
718 unsafe {
719 debug::xmlLsOneNode((*ctxt).output as *mut debug::_IO_FILE, node);
720 }
721 return 0;
722 } else if !unsafe { (*node).children }.is_null() {
723 cur = unsafe { (*node).children };
724 } else {
725 unsafe {
726 debug::xmlLsOneNode((*ctxt).output as *mut debug::_IO_FILE, node);
727 }
728 return 0;
729 }
730 while !cur.is_null() {
731 unsafe {
732 debug::xmlLsOneNode((*ctxt).output as *mut debug::_IO_FILE, cur);
733 cur = (*cur).next;
734 }
735 }
736 0
737}
738
739#[no_mangle]
757pub unsafe extern "C" fn xmlShellBase(
758 ctxt: *mut _xmlShellCtxt,
759 _arg: *mut c_char,
760 node: *mut _xmlNode,
761 _node2: *mut _xmlNode,
762) -> c_int {
763 if ctxt.is_null() {
764 return 0;
765 }
766 if node.is_null() {
767 unsafe {
768 out_bytes((*ctxt).output, b"NULL\n");
769 }
770 return 0;
771 }
772
773 let base = unsafe { xmlNodeGetBase((*node).doc, node) };
774
775 if base.is_null() {
776 unsafe {
777 out_bytes((*ctxt).output, b" No base found !!!\n");
778 }
779 } else {
780 unsafe {
781 out_cstr((*ctxt).output, base as *const c_char);
782 out_bytes((*ctxt).output, b"\n");
783 xmlFreeImpl(base as *mut c_void);
784 }
785 }
786 0
787}
788
789#[no_mangle]
807pub unsafe extern "C" fn xmlShellDir(
808 ctxt: *mut _xmlShellCtxt,
809 _arg: *mut c_char,
810 node: *mut _xmlNode,
811 _node2: *mut _xmlNode,
812) -> c_int {
813 if ctxt.is_null() {
814 return 0;
815 }
816 if node.is_null() {
817 unsafe {
818 out_bytes((*ctxt).output, b"NULL\n");
819 }
820 return 0;
821 }
822 let typ = unsafe { (*node).type_ };
823 unsafe {
824 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
825 || typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
826 {
827 debug::xmlDebugDumpDocumentHead(
828 (*ctxt).output as *mut debug::_IO_FILE,
829 node as *mut _xmlDoc,
830 );
831 } else if typ == xmlElementType::XML_ATTRIBUTE_NODE as c_int {
832 debug::xmlDebugDumpAttr(
833 (*ctxt).output as *mut debug::_IO_FILE,
834 node as *mut _xmlAttr,
835 0,
836 );
837 } else {
838 debug::xmlDebugDumpOneNode((*ctxt).output as *mut debug::_IO_FILE, node, 0);
839 }
840 }
841 0
842}
843
844#[no_mangle]
867pub unsafe extern "C" fn xmlShellCat(
868 ctxt: *mut _xmlShellCtxt,
869 _arg: *mut c_char,
870 node: *mut _xmlNode,
871 _node2: *mut _xmlNode,
872) -> c_int {
873 if ctxt.is_null() {
874 return 0;
875 }
876 if node.is_null() {
877 unsafe {
878 out_bytes((*ctxt).output, b"NULL\n");
879 }
880 return 0;
881 }
882 let out = unsafe { (*ctxt).output };
883 let is_html =
884 unsafe { (*(*ctxt).doc).type_ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int };
885 let typ = unsafe { (*node).type_ };
886 unsafe {
887 if is_html {
888 if typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int {
889 shell_html_doc_dump(out, node as *mut _xmlDoc);
890 } else {
891 shell_html_node_dump_file(out, node);
892 }
893 } else if typ == xmlElementType::XML_DOCUMENT_NODE as c_int {
894 tree::xmlDocDump(out, node as *mut _xmlDoc);
895 } else {
896 shell_elem_dump(out, (*ctxt).doc, node);
897 }
898 out_bytes(out, b"\n");
899 }
900 0
901}
902
903#[no_mangle]
921pub unsafe extern "C" fn xmlShellLoad(
922 ctxt: *mut _xmlShellCtxt,
923 filename: *mut c_char,
924 _node: *mut _xmlNode,
925 _node2: *mut _xmlNode,
926) -> c_int {
927 if ctxt.is_null() || filename.is_null() {
928 return -1;
929 }
930 let mut html = 0;
931 if !unsafe { (*ctxt).doc }.is_null() {
932 html = unsafe {
933 ((*(*ctxt).doc).type_ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int) as c_int
934 };
935 }
936
937 let doc: *mut _xmlDoc = if html != 0 {
938 unsafe { crate::xml::html::parse_file(filename, ptr::null()) }
941 } else {
942 unsafe { xmlReadFile(filename, ptr::null(), 0) }
943 };
944
945 if !doc.is_null() {
946 unsafe {
947 if (*ctxt).loaded == 1 {
948 tree::free_doc((*ctxt).doc);
949 }
950 (*ctxt).loaded = 1;
951 xmlXPathFreeContext((*ctxt).pctxt);
952 if !(*ctxt).filename.is_null() {
953 xmlFreeImpl((*ctxt).filename as *mut c_void);
954 }
955 (*ctxt).doc = doc;
956 (*ctxt).node = doc as *mut _xmlNode;
957 (*ctxt).pctxt = xmlXPathNewContext(doc);
958 (*ctxt).filename = xmlCanonicPath(filename) as *mut c_char;
959 }
960 0
961 } else {
962 -1
963 }
964}
965
966#[no_mangle]
985pub unsafe extern "C" fn xmlShellWrite(
986 ctxt: *mut _xmlShellCtxt,
987 filename: *mut c_char,
988 node: *mut _xmlNode,
989 _node2: *mut _xmlNode,
990) -> c_int {
991 if node.is_null() {
992 return -1;
993 }
994 if filename.is_null() || *filename == 0 {
995 unsafe {
996 shell_generic_error(
997 c"Write command requires a filename argument\n".as_ptr() as *const c_char,
998 b"",
999 b"",
1000 );
1001 }
1002 return -1;
1003 }
1004 if libc::access(filename, libc::W_OK) != 0 {
1006 unsafe {
1007 shell_generic_error(c"Cannot write to ".as_ptr() as *const c_char, b"", b"");
1008 shell_generic_error(filename, b"", b"\n");
1009 }
1010 return -1;
1011 }
1012 let typ = unsafe { (*node).type_ };
1013 unsafe {
1014 match typ {
1015 t if t == xmlElementType::XML_DOCUMENT_NODE as c_int => {
1016 if xmlSaveFile(filename, (*ctxt).doc) < -1 {
1017 shell_generic_error(c"Failed to write to ".as_ptr() as *const c_char, b"", b"");
1018 shell_generic_error(filename, b"", b"\n");
1019 return -1;
1020 }
1021 }
1022 t if t == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int => {
1023 if shell_save_html_doc(filename, (*ctxt).doc) < 0 {
1025 shell_generic_error(c"Failed to write to ".as_ptr() as *const c_char, b"", b"");
1026 shell_generic_error(filename, b"", b"\n");
1027 return -1;
1028 }
1029 }
1030 _ => {
1031 let f = libc::fopen(filename, c"w".as_ptr() as *const c_char);
1032 if f.is_null() {
1033 shell_generic_error(c"Failed to write to ".as_ptr() as *const c_char, b"", b"");
1034 shell_generic_error(filename, b"", b"\n");
1035 return -1;
1036 }
1037 shell_elem_dump(f as *mut c_void, (*ctxt).doc, node);
1038 libc::fclose(f);
1039 }
1040 }
1041 }
1042 0
1043}
1044
1045unsafe fn shell_save_html_doc(filename: *const c_char, doc: *mut _xmlDoc) -> c_int {
1047 if filename.is_null() || doc.is_null() {
1048 return -1;
1049 }
1050 let buf = io::buf_create(-1);
1051 if buf.is_null() {
1052 return -1;
1053 }
1054 let ret = crate::xml::html::doc_dump(buf, doc);
1055 if ret < 0 {
1056 io::buf_free(buf);
1057 return -1;
1058 }
1059 let content = io::buf_content(buf);
1060 let len = io::buf_length(buf);
1061 let written = if !content.is_null() && len > 0 {
1062 let f = libc::fopen(filename, c"w".as_ptr() as *const c_char);
1063 if f.is_null() {
1064 io::buf_free(buf);
1065 return -1;
1066 }
1067 let n = libc::fwrite(content as *const c_void, 1, len as usize, f);
1068 libc::fclose(f);
1069 n as c_int
1070 } else {
1071 0
1072 };
1073 io::buf_free(buf);
1074 written
1075}
1076
1077#[no_mangle]
1096pub unsafe extern "C" fn xmlShellSave(
1097 ctxt: *mut _xmlShellCtxt,
1098 filename: *mut c_char,
1099 _node: *mut _xmlNode,
1100 _node2: *mut _xmlNode,
1101) -> c_int {
1102 if ctxt.is_null() || unsafe { (*ctxt).doc }.is_null() {
1103 return -1;
1104 }
1105 let mut filename = filename;
1106 if filename.is_null() || *filename == 0 {
1107 filename = unsafe { (*ctxt).filename };
1108 }
1109 if filename.is_null() {
1110 return -1;
1111 }
1112 if libc::access(filename, libc::W_OK) != 0 {
1114 unsafe {
1115 shell_generic_error(c"Cannot save to ".as_ptr() as *const c_char, b"", b"");
1116 shell_generic_error(filename, b"", b"\n");
1117 }
1118 return -1;
1119 }
1120 let typ = unsafe { (*(*ctxt).doc).type_ };
1121 unsafe {
1122 match typ {
1123 t if t == xmlElementType::XML_DOCUMENT_NODE as c_int => {
1124 if xmlSaveFile(filename, (*ctxt).doc) < 0 {
1125 shell_generic_error(c"Failed to save to ".as_ptr() as *const c_char, b"", b"");
1126 shell_generic_error(filename, b"", b"\n");
1127 }
1128 }
1129 t if t == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int => {
1130 if shell_save_html_doc(filename, (*ctxt).doc) < 0 {
1131 shell_generic_error(c"Failed to save to ".as_ptr() as *const c_char, b"", b"");
1132 shell_generic_error(filename, b"", b"\n");
1133 }
1134 }
1135 _ => {
1136 shell_generic_error(
1137 c"To save to subparts of a document use the 'write' command\n".as_ptr()
1138 as *const c_char,
1139 b"",
1140 b"",
1141 );
1142 return -1;
1143 }
1144 }
1145 }
1146 0
1147}
1148
1149unsafe extern "C" fn shell_valid_error(_ctx: *mut c_void, msg: *const c_char) {
1156 unsafe {
1157 if !msg.is_null() {
1158 out_cstr(stderr, msg);
1159 }
1160 }
1161}
1162
1163unsafe fn shell_parse_dtd(dtd: *const c_char) -> *mut _xmlDtd {
1171 if dtd.is_null() {
1172 return ptr::null_mut();
1173 }
1174 let path = match unsafe { core::ffi::CStr::from_ptr(dtd) }.to_str() {
1175 Ok(p) => p,
1176 Err(_) => return ptr::null_mut(),
1177 };
1178 let content = match std::fs::read(path) {
1179 Ok(c) => c,
1180 Err(_) => return ptr::null_mut(),
1181 };
1182 let lower: Vec<u8> = content.iter().map(|b| b.to_ascii_lowercase()).collect();
1184 let pos = match find_subslice(&lower, b"<!doctype") {
1185 Some(p) => p,
1186 None => {
1189 let base = path.rsplit(['/', '\\']).next().unwrap_or(path);
1190 let name_c = bytes_to_xmlstr(base.as_bytes());
1191 let dtd_node =
1192 crate::xml::dtd::new_dtd(ptr::null_mut(), name_c, ptr::null_mut(), ptr::null_mut());
1193 if !name_c.is_null() {
1194 xmlFreeImpl(name_c as *mut c_void);
1195 }
1196 return dtd_node;
1197 }
1198 };
1199 let mut i = pos + b"<!doctype".len();
1200 while i < content.len() && (content[i] as char).is_ascii_whitespace() {
1202 i += 1;
1203 }
1204 let name_start = i;
1206 while i < content.len()
1207 && !(content[i] as char).is_ascii_whitespace()
1208 && content[i] != b'>'
1209 && content[i] != b'['
1210 {
1211 i += 1;
1212 }
1213 if i == name_start {
1214 return ptr::null_mut();
1215 }
1216 let name = &content[name_start..i];
1217
1218 while i < content.len() && (content[i] as char).is_ascii_whitespace() {
1220 i += 1;
1221 }
1222 let mut public_id: Option<&[u8]> = None;
1223 let mut system_id: Option<&[u8]> = None;
1224 if i < content.len() && content[i..].to_ascii_lowercase().starts_with(b"public") {
1225 i += b"public".len();
1226 while i < content.len() && (content[i] as char).is_ascii_whitespace() {
1227 i += 1;
1228 }
1229 if i < content.len() && content[i] == b'"' {
1230 let s = i + 1;
1231 let e = content[s..].iter().position(|&c| c == b'"').map(|p| s + p);
1232 if let Some(e) = e {
1233 public_id = Some(&content[s..e]);
1234 }
1235 }
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 system_id = Some(&content[s..e]);
1244 }
1245 }
1246 } else if i < content.len() && content[i..].to_ascii_lowercase().starts_with(b"system") {
1247 i += b"system".len();
1248 while i < content.len() && (content[i] as char).is_ascii_whitespace() {
1249 i += 1;
1250 }
1251 if i < content.len() && content[i] == b'"' {
1252 let s = i + 1;
1253 let e = content[s..].iter().position(|&c| c == b'"').map(|p| s + p);
1254 if let Some(e) = e {
1255 system_id = Some(&content[s..e]);
1256 }
1257 }
1258 }
1259
1260 let name_c = bytes_to_xmlstr(name);
1261 let ext_c = match public_id {
1262 Some(v) => bytes_to_xmlstr(v),
1263 None => ptr::null_mut(),
1264 };
1265 let sys_c = match system_id {
1266 Some(v) => bytes_to_xmlstr(v),
1267 None => ptr::null_mut(),
1268 };
1269 let dtd_node = crate::xml::dtd::new_dtd(ptr::null_mut(), name_c, ext_c, sys_c);
1270 if !name_c.is_null() {
1271 xmlFreeImpl(name_c as *mut c_void);
1272 }
1273 if !ext_c.is_null() {
1274 xmlFreeImpl(ext_c as *mut c_void);
1275 }
1276 if !sys_c.is_null() {
1277 xmlFreeImpl(sys_c as *mut c_void);
1278 }
1279 dtd_node
1280}
1281
1282fn find_subslice(haystack: &[u8], needle: &[u8]) -> Option<usize> {
1284 if needle.is_empty() || haystack.len() < needle.len() {
1285 return None;
1286 }
1287 haystack.windows(needle.len()).position(|w| w == needle)
1288}
1289
1290unsafe fn bytes_to_xmlstr(bytes: &[u8]) -> *mut xmlChar {
1292 let buf = xmlMallocImpl(bytes.len() + 1) as *mut xmlChar;
1293 if buf.is_null() {
1294 return ptr::null_mut();
1295 }
1296 unsafe {
1297 ptr::copy_nonoverlapping(bytes.as_ptr(), buf, bytes.len());
1298 *buf.add(bytes.len()) = 0;
1299 }
1300 buf
1301}
1302
1303#[no_mangle]
1321pub unsafe extern "C" fn xmlShellValidate(
1322 ctxt: *mut _xmlShellCtxt,
1323 dtd: *mut c_char,
1324 _node: *mut _xmlNode,
1325 _node2: *mut _xmlNode,
1326) -> c_int {
1327 if ctxt.is_null() || unsafe { (*ctxt).doc }.is_null() {
1328 return -1;
1329 }
1330 let vctxt = xmlMallocZero(size_of::<_xmlValidCtxt>()) as *mut _xmlValidCtxt;
1332 if vctxt.is_null() {
1333 return -1;
1334 }
1335 unsafe {
1336 (*vctxt).error = Some(shell_valid_error);
1337 (*vctxt).warning = Some(shell_valid_error);
1338 }
1339 let mut res = -1;
1340 unsafe {
1341 if dtd.is_null() || *dtd == 0 {
1342 res = xmlValidateDocument(vctxt, (*ctxt).doc);
1343 } else {
1344 let subset = shell_parse_dtd(dtd as *const c_char);
1345 if !subset.is_null() {
1346 res = xmlValidateDtd(vctxt, (*ctxt).doc, subset);
1347 crate::xml::dtd::free_dtd(subset);
1348 }
1349 }
1350 }
1351 unsafe {
1352 xmlFreeImpl(vctxt as *mut c_void);
1353 }
1354 res
1355}
1356
1357#[no_mangle]
1380pub unsafe extern "C" fn xmlShellDu(
1381 ctxt: *mut _xmlShellCtxt,
1382 _arg: *mut c_char,
1383 tree: *mut _xmlNode,
1384 _node2: *mut _xmlNode,
1385) -> c_int {
1386 if ctxt.is_null() {
1387 return -1;
1388 }
1389 if tree.is_null() {
1390 return -1;
1391 }
1392 let out = unsafe { (*ctxt).output };
1393 let mut indent: c_int = 0;
1394 let mut node: *mut _xmlNode = tree;
1395 unsafe {
1396 while !node.is_null() {
1397 let typ = (*node).type_;
1398 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
1399 || typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
1400 {
1401 out_bytes(out, b"/\n");
1402 } else if typ == xmlElementType::XML_ELEMENT_NODE as c_int {
1403 let mut line = Vec::new();
1404 for _ in 0..indent {
1405 line.extend_from_slice(b" ");
1406 }
1407 if !(*node).ns.is_null() && !(*(*node).ns).prefix.is_null() {
1408 push_cstr(&mut line, (*(*node).ns).prefix as *const c_char);
1409 line.push(b':');
1410 }
1411 push_cstr(&mut line, (*node).name as *const c_char);
1412 line.push(b'\n');
1413 out_bytes(out, &line);
1414 }
1415
1416 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
1420 || typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
1421 {
1422 node = (*(node as *mut _xmlDoc)).children;
1423 } else if !(*node).children.is_null()
1424 && typ != xmlElementType::XML_ENTITY_REF_NODE as c_int
1425 {
1426 node = (*node).children;
1427 indent += 1;
1428 } else if node != tree && !(*node).next.is_null() {
1429 node = (*node).next;
1430 } else if node != tree {
1431 while node != tree {
1432 if !(*node).parent.is_null() {
1433 node = (*node).parent;
1434 indent -= 1;
1435 }
1436 if node != tree && !(*node).next.is_null() {
1437 node = (*node).next;
1438 break;
1439 }
1440 if (*node).parent.is_null() {
1441 node = ptr::null_mut();
1442 break;
1443 }
1444 if node == tree {
1445 node = ptr::null_mut();
1446 break;
1447 }
1448 }
1449 if node == tree {
1450 node = ptr::null_mut();
1451 }
1452 } else {
1453 node = ptr::null_mut();
1454 }
1455 }
1456 }
1457 0
1458}
1459
1460#[no_mangle]
1479pub unsafe extern "C" fn xmlShellPwd(
1480 _ctxt: *mut _xmlShellCtxt,
1481 buffer: *mut c_char,
1482 node: *mut _xmlNode,
1483 _node2: *mut _xmlNode,
1484) -> c_int {
1485 if node.is_null() || buffer.is_null() {
1486 return -1;
1487 }
1488
1489 let path = unsafe { shell_get_node_path(node) };
1490 if path.is_null() {
1491 return -1;
1492 }
1493
1494 let plen = unsafe { tree::xml_strlen(path) } as usize;
1496 let n = plen.min(498);
1497 unsafe {
1498 ptr::copy_nonoverlapping(path as *const u8, buffer as *mut u8, n);
1499 *buffer.add(n) = 0;
1500 *buffer.add(499) = b'0' as c_char;
1501 }
1502 unsafe {
1503 xmlFreeImpl(path as *mut c_void);
1504 }
1505 0
1506}
1507
1508unsafe fn xmlShellSetBase(ctxt: *mut _xmlShellCtxt, arg: *mut c_char, node: *mut _xmlNode) {
1514 let _ = ctxt;
1515 if !node.is_null() {
1516 unsafe {
1517 xmlNodeSetBase(node, arg as *const xmlChar);
1518 }
1519 }
1520}
1521
1522unsafe fn xmlShellRegisterNamespace(ctxt: *mut _xmlShellCtxt, arg: *mut c_char) -> c_int {
1525 let ns_list_dup = unsafe { xmlStrdup(arg as *const xmlChar) };
1526 if ns_list_dup.is_null() {
1527 return -1;
1528 }
1529 let mut next: *mut xmlChar = ns_list_dup;
1530 loop {
1531 if unsafe { *next == 0 } {
1532 break;
1533 }
1534 let prefix = next;
1536 let eq = unsafe { xmlStrchr(next, b'=' as xmlChar) };
1537 if eq.is_null() {
1538 unsafe {
1539 out_cstr(
1540 (*ctxt).output,
1541 c"setns: prefix=[nsuri] required\n".as_ptr() as *const c_char,
1542 );
1543 }
1544 unsafe {
1545 xmlFreeImpl(ns_list_dup as *mut c_void);
1546 }
1547 return -1;
1548 }
1549 unsafe {
1551 *(eq as *mut xmlChar) = 0;
1552 }
1553 let href = unsafe { eq.add(1) };
1554 let space = unsafe { xmlStrchr(href, b' ' as xmlChar) };
1556 if !space.is_null() {
1557 unsafe {
1558 *(space as *mut xmlChar) = 0;
1559 }
1560 next = unsafe { space.add(1) as *mut xmlChar };
1561 } else {
1562 next = unsafe { href.add(tree::xml_strlen(href) as usize) as *mut xmlChar };
1563 }
1564
1565 if unsafe { xmlXPathRegisterNs((*ctxt).pctxt, prefix, href) } != 0 {
1567 unsafe {
1568 let mut msg = Vec::new();
1569 msg.extend_from_slice(b"Error: unable to register NS with prefix=\"");
1570 push_cstr(&mut msg, prefix as *const c_char);
1571 msg.extend_from_slice(b"\" and href=\"");
1572 push_cstr(&mut msg, href as *const c_char);
1573 msg.extend_from_slice(b"\"\n");
1574 out_bytes((*ctxt).output, &msg);
1575 }
1576 unsafe {
1577 xmlFreeImpl(ns_list_dup as *mut c_void);
1578 }
1579 return -1;
1580 }
1581 }
1582 unsafe {
1583 xmlFreeImpl(ns_list_dup as *mut c_void);
1584 }
1585 0
1586}
1587
1588unsafe fn xmlShellRegisterRootNamespaces(ctxt: *mut _xmlShellCtxt, root: *mut _xmlNode) -> c_int {
1591 if root.is_null()
1592 || unsafe { (*root).type_ != xmlElementType::XML_ELEMENT_NODE as c_int }
1593 || unsafe { (*root).nsDef.is_null() }
1594 || ctxt.is_null()
1595 || unsafe { (*ctxt).pctxt.is_null() }
1596 {
1597 return -1;
1598 }
1599 let mut ns = unsafe { (*root).nsDef };
1600 while !ns.is_null() {
1601 if unsafe { (*ns).prefix.is_null() } {
1602 unsafe {
1603 xmlXPathRegisterNs(
1604 (*ctxt).pctxt,
1605 c"defaultns".as_ptr() as *const xmlChar,
1606 (*ns).href,
1607 );
1608 }
1609 } else {
1610 unsafe {
1611 xmlXPathRegisterNs((*ctxt).pctxt, (*ns).prefix, (*ns).href);
1612 }
1613 }
1614 ns = unsafe { (*ns).next };
1615 }
1616 0
1617}
1618
1619unsafe fn xmlShellGrep(ctxt: *mut _xmlShellCtxt, arg: *mut c_char, node: *mut _xmlNode) {
1622 if ctxt.is_null() || node.is_null() || arg.is_null() {
1623 return;
1624 }
1625 let mut node = node;
1626 while !node.is_null() {
1627 unsafe {
1628 let typ = (*node).type_;
1629 if typ == xmlElementType::XML_COMMENT_NODE as c_int {
1630 if !xmlStrstr((*node).content, arg as *const xmlChar).is_null() {
1631 let path = shell_get_node_path(node);
1632 if !path.is_null() {
1633 let mut line = Vec::new();
1634 push_cstr(&mut line, path as *const c_char);
1635 line.extend_from_slice(b" : ");
1636 out_bytes((*ctxt).output, &line);
1637 xmlFreeImpl(path as *mut c_void);
1638 }
1639 xmlShellList(ctxt, ptr::null_mut(), node, ptr::null_mut());
1640 }
1641 } else if typ == xmlElementType::XML_TEXT_NODE as c_int
1642 && !xmlStrstr((*node).content, arg as *const xmlChar).is_null()
1643 {
1644 let path = shell_get_node_path((*node).parent);
1645 if !path.is_null() {
1646 let mut line = Vec::new();
1647 push_cstr(&mut line, path as *const c_char);
1648 line.extend_from_slice(b" : ");
1649 out_bytes((*ctxt).output, &line);
1650 xmlFreeImpl(path as *mut c_void);
1651 }
1652 xmlShellList(ctxt, ptr::null_mut(), (*node).parent, ptr::null_mut());
1653 }
1654
1655 if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
1659 || typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
1660 {
1661 node = (*(node as *mut _xmlDoc)).children;
1662 } else if !(*node).children.is_null()
1663 && typ != xmlElementType::XML_ENTITY_REF_NODE as c_int
1664 {
1665 node = (*node).children;
1666 } else if !(*node).next.is_null() {
1667 node = (*node).next;
1668 } else {
1669 while !node.is_null() {
1670 if !(*node).parent.is_null() {
1671 node = (*node).parent;
1672 }
1673 if !(*node).next.is_null() {
1674 node = (*node).next;
1675 break;
1676 }
1677 if (*node).parent.is_null() {
1678 node = ptr::null_mut();
1679 break;
1680 }
1681 }
1682 }
1683 }
1684 }
1685}
1686
1687unsafe fn shell_result_type_error(arg: *const c_char, typ: c_int) {
1690 if typ == xmlXPathObjectType::XPATH_UNDEFINED as c_int {
1691 unsafe { shell_generic_error(arg, b": ", b"no such node\n") };
1692 } else if typ == xmlXPathObjectType::XPATH_BOOLEAN as c_int {
1693 unsafe { shell_generic_error(arg, b" is a Boolean", b"\n") };
1694 } else if typ == xmlXPathObjectType::XPATH_NUMBER as c_int {
1695 unsafe { shell_generic_error(arg, b" is a number", b"\n") };
1696 } else if typ == xmlXPathObjectType::XPATH_STRING as c_int {
1697 unsafe { shell_generic_error(arg, b" is a string", b"\n") };
1698 } else if typ == xmlXPathObjectType::XPATH_POINT as c_int {
1699 unsafe { shell_generic_error(arg, b" is a point", b"\n") };
1700 } else if typ == xmlXPathObjectType::XPATH_RANGE as c_int
1701 || typ == xmlXPathObjectType::XPATH_LOCATIONSET as c_int
1702 {
1703 unsafe { shell_generic_error(arg, b" is a range", b"\n") };
1704 } else if typ == xmlXPathObjectType::XPATH_USERS as c_int {
1705 unsafe { shell_generic_error(arg, b" is user-defined", b"\n") };
1706 } else if typ == xmlXPathObjectType::XPATH_XSLT_TREE as c_int {
1707 unsafe { shell_generic_error(arg, b" is an XSLT value tree", b"\n") };
1708 }
1709}
1710
1711unsafe fn shell_build_prompt(ctxt: *mut _xmlShellCtxt) -> Vec<u8> {
1718 let mut p = Vec::new();
1719 let node = unsafe { (*ctxt).node };
1720 let doc = unsafe { (*ctxt).doc };
1721 if node == doc as *mut _xmlNode {
1722 p.extend_from_slice(b"/ > ");
1723 } else if !node.is_null() && !unsafe { (*node).name }.is_null() {
1724 unsafe {
1725 if !(*node).ns.is_null() && !(*(*node).ns).prefix.is_null() {
1726 push_cstr(&mut p, (*(*node).ns).prefix as *const c_char);
1727 p.push(b':');
1728 }
1729 push_cstr(&mut p, (*node).name as *const c_char);
1730 }
1731 p.extend_from_slice(b" > ");
1732 } else {
1733 p.extend_from_slice(b"? > ");
1734 }
1735 p.push(0);
1736 p
1737}
1738
1739unsafe fn shell_print_help(ctxt: *mut _xmlShellCtxt) {
1741 let out = unsafe { (*ctxt).output };
1742 const HELP: &[&[u8]] = &[
1743 b"\tbase display XML base of the node\n",
1744 b"\tsetbase URI change the XML base of the node\n",
1745 b"\tbye leave shell\n",
1746 b"\tcat [node] display node or current node\n",
1747 b"\tcd [path] change directory to path or to root\n",
1748 b"\tdir [path] dumps information about the node (namespace, attributes, content)\n",
1749 b"\tdu [path] show the structure of the subtree under path or the current node\n",
1750 b"\texit leave shell\n",
1751 b"\thelp display this help\n",
1752 b"\tfree display memory usage\n",
1753 b"\tload [name] load a new document with name\n",
1754 b"\tls [path] list contents of path or the current directory\n",
1755 b"\txpath expr evaluate the XPath expression in that context and print the result\n",
1756 b"\tsetns nsreg register a namespace to a prefix in the XPath evaluation context\n",
1757 b"\t format for nsreg is: prefix=[nsuri] (i.e. prefix= unsets a prefix)\n",
1758 b"\tsetrootns register all namespace found on the root element\n",
1759 b"\t the default namespace if any uses 'defaultns' prefix\n",
1760 b"\tpwd display current working directory\n",
1761 b"\twhereis display absolute path of [path] or current working directory\n",
1762 b"\tquit leave shell\n",
1763 b"\tsave [name] save this document to name or the original name\n",
1764 b"\twrite [name] write the current node to the filename\n",
1765 b"\tvalidate check the document for errors\n",
1766 b"\tgrep string search for a string in the subtree\n",
1767 ];
1768 for line in HELP {
1769 unsafe {
1770 out_bytes(out, line);
1771 }
1772 }
1773}
1774
1775#[no_mangle]
1794pub unsafe extern "C" fn xmlShell(
1795 doc: *mut _xmlDoc,
1796 filename: *mut c_char,
1797 input: xmlShellReadlineFunc,
1798 output: *mut c_void,
1799) {
1800 if doc.is_null() || filename.is_null() || input.is_none() {
1801 return;
1802 }
1803 let output = if output.is_null() {
1804 unsafe { stdout }
1805 } else {
1806 output
1807 };
1808
1809 let ctxt = xmlMallocZero(size_of::<_xmlShellCtxt>()) as *mut _xmlShellCtxt;
1810 if ctxt.is_null() {
1811 return;
1812 }
1813 unsafe {
1814 (*ctxt).loaded = 0;
1815 (*ctxt).doc = doc;
1816 (*ctxt).input = input;
1817 (*ctxt).output = output;
1818 (*ctxt).filename = xmlStrdup(filename as *const xmlChar) as *mut c_char;
1819 (*ctxt).node = doc as *mut _xmlNode;
1820 (*ctxt).pctxt = xmlXPathNewContext(doc);
1821 }
1822 if unsafe { (*ctxt).pctxt }.is_null() {
1823 unsafe {
1824 xmlFreeImpl(ctxt as *mut c_void);
1825 }
1826 return;
1827 }
1828
1829 let mut cmdline: *mut c_char = ptr::null_mut();
1830 loop {
1831 let prompt = unsafe { shell_build_prompt(ctxt) };
1833 let readline = unsafe { (*ctxt).input };
1834 cmdline = match readline {
1835 Some(f) => f(prompt.as_ptr() as *mut c_char),
1836 None => break,
1837 };
1838 if cmdline.is_null() {
1839 break;
1840 }
1841
1842 let clen = unsafe { libc::strlen(cmdline) } as usize;
1844 let cbytes = unsafe { core::slice::from_raw_parts(cmdline as *const u8, clen) };
1845 let mut i = 0usize;
1846 while i < clen && (cbytes[i] == b' ' || cbytes[i] == b'\t') {
1847 i += 1;
1848 }
1849 let mut command: Vec<u8> = Vec::new();
1850 while i < clen
1851 && cbytes[i] != b' '
1852 && cbytes[i] != b'\t'
1853 && cbytes[i] != b'\n'
1854 && cbytes[i] != b'\r'
1855 {
1856 command.push(cbytes[i]);
1857 i += 1;
1858 }
1859 if command.is_empty() {
1860 unsafe {
1861 libc::free(cmdline as *mut c_void);
1862 }
1863 cmdline = ptr::null_mut();
1864 continue;
1865 }
1866
1867 while i < clen && (cbytes[i] == b' ' || cbytes[i] == b'\t') {
1869 i += 1;
1870 }
1871 let mut arg: Vec<u8> = Vec::new();
1872 while i < clen && cbytes[i] != b'\n' && cbytes[i] != b'\r' {
1873 arg.push(cbytes[i]);
1874 i += 1;
1875 }
1876
1877 command.push(0);
1879 let cmd: &[u8] = &command;
1880 let mut argn = arg.clone();
1881 argn.push(0);
1882 let arg_cstr: *mut c_char = argn.as_mut_ptr() as *mut c_char;
1883 let arg_xml: *const xmlChar = argn.as_ptr() as *const xmlChar;
1884
1885 if cmd == b"exit\0" || cmd == b"quit\0" || cmd == b"bye\0" {
1887 break;
1888 }
1889 if cmd == b"help\0" {
1890 unsafe { shell_print_help(ctxt) };
1891 } else if cmd == b"validate\0" {
1892 unsafe {
1893 xmlShellValidate(ctxt, arg_cstr, ptr::null_mut(), ptr::null_mut());
1894 }
1895 } else if cmd == b"load\0" {
1896 unsafe {
1897 xmlShellLoad(ctxt, arg_cstr, ptr::null_mut(), ptr::null_mut());
1898 }
1899 } else if cmd == b"save\0" {
1900 unsafe {
1901 xmlShellSave(ctxt, arg_cstr, ptr::null_mut(), ptr::null_mut());
1902 }
1903 } else if cmd == b"write\0" {
1904 if arg.is_empty() {
1905 unsafe {
1906 shell_generic_error(
1907 c"Write command requires a filename argument\n".as_ptr() as *const c_char,
1908 b"",
1909 b"",
1910 );
1911 }
1912 } else {
1913 unsafe {
1914 xmlShellWrite(ctxt, arg_cstr, (*ctxt).node, ptr::null_mut());
1915 }
1916 }
1917 } else if cmd == b"grep\0" {
1918 unsafe {
1919 xmlShellGrep(ctxt, arg_cstr, (*ctxt).node);
1920 }
1921 } else if cmd == b"free\0" {
1922 unsafe {
1923 if arg.is_empty() {
1924 crate::abi::allocator::xmlMemShow((*ctxt).output, 0);
1925 } else {
1926 let mut len: c_int = 0;
1927 let arg_s = core::str::from_utf8(&argn[..argn.len() - 1]).unwrap_or("");
1928 if let Ok(v) = arg_s.trim().parse::<c_int>() {
1929 len = v;
1930 }
1931 crate::abi::allocator::xmlMemShow((*ctxt).output, len);
1932 }
1933 }
1934 } else if cmd == b"pwd\0" {
1935 let mut dir = [0 as c_char; 500];
1936 unsafe {
1937 if xmlShellPwd(ctxt, dir.as_mut_ptr(), (*ctxt).node, ptr::null_mut()) == 0 {
1938 let mut line = Vec::new();
1939 push_cstr(&mut line, dir.as_mut_ptr());
1940 line.extend_from_slice(b"\n");
1941 out_bytes((*ctxt).output, &line);
1942 }
1943 }
1944 } else if cmd == b"du\0" {
1945 unsafe {
1946 if arg.is_empty() {
1947 xmlShellDu(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
1948 } else {
1949 (*(*ctxt).pctxt).node = (*ctxt).node;
1950 let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
1951 if !list.is_null() {
1952 let typ = (*list).type_;
1953 if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
1954 let ns = (*list).nodesetval as *mut _xmlNodeSet;
1955 if !ns.is_null() {
1956 for indx in 0..(*ns).nodeNr {
1957 let n = *(*ns).nodeTab.add(indx as usize);
1958 xmlShellDu(ctxt, ptr::null_mut(), n, ptr::null_mut());
1959 }
1960 }
1961 } else {
1962 shell_result_type_error(arg_cstr, typ);
1963 }
1964 xmlXPathFreeObject(list);
1965 } else {
1966 shell_generic_error(arg_cstr, b": ", b"no such node\n");
1967 }
1968 (*(*ctxt).pctxt).node = ptr::null_mut();
1969 }
1970 }
1971 } else if cmd == b"base\0" {
1972 unsafe {
1973 xmlShellBase(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
1974 }
1975 } else if cmd == b"setns\0" {
1976 unsafe {
1977 if arg.is_empty() {
1978 shell_generic_error(
1979 c"setns: prefix=[nsuri] required\n".as_ptr() as *const c_char,
1980 b"",
1981 b"",
1982 );
1983 } else {
1984 xmlShellRegisterNamespace(ctxt, arg_cstr);
1985 }
1986 }
1987 } else if cmd == b"setrootns\0" {
1988 unsafe {
1989 let root = tree::doc_get_root_element((*ctxt).doc);
1990 xmlShellRegisterRootNamespaces(ctxt, root);
1991 }
1992 } else if cmd == b"xpath\0" {
1993 unsafe {
1994 if arg.is_empty() {
1995 shell_generic_error(
1996 c"xpath: expression required\n".as_ptr() as *const c_char,
1997 b"",
1998 b"",
1999 );
2000 } else {
2001 (*(*ctxt).pctxt).node = (*ctxt).node;
2002 let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
2003 xmlXPathDebugDumpObject((*ctxt).output, list, 0);
2004 xmlXPathFreeObject(list);
2005 }
2006 }
2007 } else if cmd == b"setbase\0" {
2008 unsafe {
2009 xmlShellSetBase(ctxt, arg_cstr, (*ctxt).node);
2010 }
2011 } else if cmd == b"ls\0" || cmd == b"dir\0" {
2012 let is_dir = cmd == b"dir\0";
2013 unsafe {
2014 if arg.is_empty() {
2015 if is_dir {
2016 xmlShellDir(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
2017 } else {
2018 xmlShellList(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
2019 }
2020 } else {
2021 (*(*ctxt).pctxt).node = (*ctxt).node;
2022 let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
2023 if !list.is_null() {
2024 let typ = (*list).type_;
2025 if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
2026 let ns = (*list).nodesetval as *mut _xmlNodeSet;
2027 if !ns.is_null() {
2028 for indx in 0..(*ns).nodeNr {
2029 let n = *(*ns).nodeTab.add(indx as usize);
2030 if is_dir {
2031 xmlShellDir(ctxt, ptr::null_mut(), n, ptr::null_mut());
2032 } else {
2033 xmlShellList(ctxt, ptr::null_mut(), n, ptr::null_mut());
2034 }
2035 }
2036 }
2037 } else {
2038 shell_result_type_error(arg_cstr, typ);
2039 }
2040 xmlXPathFreeObject(list);
2041 } else {
2042 shell_generic_error(arg_cstr, b": ", b"no such node\n");
2043 }
2044 (*(*ctxt).pctxt).node = ptr::null_mut();
2045 }
2046 }
2047 } else if cmd == b"whereis\0" {
2048 let mut dir = [0 as c_char; 500];
2049 unsafe {
2050 if arg.is_empty() {
2051 if xmlShellPwd(ctxt, dir.as_mut_ptr(), (*ctxt).node, ptr::null_mut()) == 0 {
2052 let mut line = Vec::new();
2053 push_cstr(&mut line, dir.as_mut_ptr());
2054 line.extend_from_slice(b"\n");
2055 out_bytes((*ctxt).output, &line);
2056 }
2057 } else {
2058 (*(*ctxt).pctxt).node = (*ctxt).node;
2059 let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
2060 if !list.is_null() {
2061 let typ = (*list).type_;
2062 if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
2063 let ns = (*list).nodesetval as *mut _xmlNodeSet;
2064 if !ns.is_null() {
2065 for indx in 0..(*ns).nodeNr {
2066 let n = *(*ns).nodeTab.add(indx as usize);
2067 if xmlShellPwd(ctxt, dir.as_mut_ptr(), n, ptr::null_mut()) == 0
2068 {
2069 let mut line = Vec::new();
2070 push_cstr(&mut line, dir.as_mut_ptr());
2071 line.extend_from_slice(b"\n");
2072 out_bytes((*ctxt).output, &line);
2073 }
2074 }
2075 }
2076 } else {
2077 shell_result_type_error(arg_cstr, typ);
2078 }
2079 xmlXPathFreeObject(list);
2080 } else {
2081 shell_generic_error(arg_cstr, b": ", b"no such node\n");
2082 }
2083 (*(*ctxt).pctxt).node = ptr::null_mut();
2084 }
2085 }
2086 } else if cmd == b"cd\0" {
2087 unsafe {
2088 if arg.is_empty() {
2089 (*ctxt).node = (*ctxt).doc as *mut _xmlNode;
2090 } else {
2091 let mut argn = argn;
2093 let l = argn.len();
2094 if l >= 3 && argn[l - 2] == b'/' {
2095 argn[l - 2] = 0;
2096 }
2097 (*(*ctxt).pctxt).node = (*ctxt).node;
2098 let list = xmlXPathEval(argn.as_ptr() as *const xmlChar, (*ctxt).pctxt);
2099 if !list.is_null() {
2100 let typ = (*list).type_;
2101 if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
2102 let ns = (*list).nodesetval as *mut _xmlNodeSet;
2103 if !ns.is_null() {
2104 if (*ns).nodeNr == 1 {
2105 (*ctxt).node = *(*ns).nodeTab;
2106 if !(*ctxt).node.is_null()
2107 && (*(*ctxt).node).type_
2108 == xmlElementType::XML_NAMESPACE_DECL as c_int
2109 {
2110 shell_generic_error(
2111 c"cannot cd to namespace\n".as_ptr() as *const c_char,
2112 b"",
2113 b"",
2114 );
2115 (*ctxt).node = ptr::null_mut();
2116 }
2117 } else {
2118 let mut msg = Vec::new();
2119 push_cstr(&mut msg, arg_cstr);
2120 msg.extend_from_slice(b" is a ");
2121 msg.extend_from_slice((*ns).nodeNr.to_string().as_bytes());
2122 msg.extend_from_slice(b" Node Set\n");
2123 out_bytes(stderr, &msg);
2124 }
2125 } else {
2126 let mut msg = Vec::new();
2127 push_cstr(&mut msg, arg_cstr);
2128 msg.extend_from_slice(b" is an empty Node Set\n");
2129 out_bytes(stderr, &msg);
2130 }
2131 } else {
2132 shell_result_type_error(arg_cstr, typ);
2133 }
2134 xmlXPathFreeObject(list);
2135 } else {
2136 shell_generic_error(arg_cstr, b": ", b"no such node\n");
2137 }
2138 (*(*ctxt).pctxt).node = ptr::null_mut();
2139 }
2140 }
2141 } else if cmd == b"cat\0" {
2142 unsafe {
2143 if arg.is_empty() {
2144 xmlShellCat(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
2145 } else {
2146 (*(*ctxt).pctxt).node = (*ctxt).node;
2151 let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
2152 if !list.is_null() {
2153 let typ = (*list).type_;
2154 if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
2155 let ns = (*list).nodesetval as *mut _xmlNodeSet;
2156 if !ns.is_null() {
2157 for indx in 0..(*ns).nodeNr {
2158 if i > 0 {
2159 out_bytes((*ctxt).output, b" -------\n");
2160 }
2161 let n = *(*ns).nodeTab.add(indx as usize);
2162 xmlShellCat(ctxt, ptr::null_mut(), n, ptr::null_mut());
2163 }
2164 }
2165 } else {
2166 shell_result_type_error(arg_cstr, typ);
2167 }
2168 xmlXPathFreeObject(list);
2169 } else {
2170 shell_generic_error(arg_cstr, b": ", b"no such node\n");
2171 }
2172 (*(*ctxt).pctxt).node = ptr::null_mut();
2173 }
2174 }
2175 } else {
2176 let mut msg = Vec::new();
2177 msg.extend_from_slice(b"Unknown command ");
2178 msg.extend_from_slice(&command[..command.len() - 1]);
2179 msg.extend_from_slice(b"\n");
2180 unsafe {
2181 out_bytes(stderr, &msg);
2182 }
2183 }
2184
2185 unsafe {
2186 libc::free(cmdline as *mut c_void);
2187 }
2188 cmdline = ptr::null_mut();
2189 }
2190
2191 unsafe {
2193 xmlXPathFreeContext((*ctxt).pctxt);
2194 if (*ctxt).loaded != 0 {
2195 tree::free_doc((*ctxt).doc);
2196 }
2197 if !(*ctxt).filename.is_null() {
2198 xmlFreeImpl((*ctxt).filename as *mut c_void);
2199 }
2200 xmlFreeImpl(ctxt as *mut c_void);
2201 if !cmdline.is_null() {
2202 libc::free(cmdline as *mut c_void);
2203 }
2204 }
2205}