#![allow(
missing_docs,
missing_debug_implementations,
non_snake_case,
non_camel_case_types,
non_upper_case_globals
)]
use core::ffi::c_void;
use core::ptr;
use std::os::raw::{c_char, c_int};
use crate::abi::allocator::{xmlFreeImpl, xmlMallocImpl, xmlMallocZero};
use crate::abi::exports_string::xmlStrstr;
use crate::abi::exports_tree::{xmlNodeGetBase, xmlNodeSetBase};
use crate::abi::exports_uri::xmlCanonicPath;
use crate::abi::exports_xml2::{
xmlReadFile, xmlSaveFile, xmlStrEqual, xmlStrchr, xmlStrdup, xmlValidateDocument,
xmlValidateDtd, xmlXPathEval, xmlXPathFreeContext, xmlXPathFreeObject, xmlXPathNewContext,
xmlXPathRegisterNs,
};
use crate::abi::structs::{
_xmlAttr, _xmlDoc, _xmlDtd, _xmlNode, _xmlNodeSet, _xmlValidCtxt, _xmlXPathContext,
_xmlXPathObject,
};
use crate::abi::types::{xmlChar, xmlElementType, xmlXPathObjectType};
use crate::xml::xpath::exports::xmlXPathDebugDumpObject;
use crate::xml::{debug, io, tree};
pub type xmlShellReadlineFunc = Option<unsafe extern "C" fn(prompt: *mut c_char) -> *mut c_char>;
#[repr(C)]
pub struct _xmlShellCtxt {
pub filename: *mut c_char,
pub doc: *mut _xmlDoc,
pub node: *mut _xmlNode,
pub pctxt: *mut _xmlXPathContext,
pub loaded: c_int,
pub output: *mut c_void,
pub input: xmlShellReadlineFunc,
}
pub type xmlShellCmd = Option<
unsafe extern "C" fn(*mut _xmlShellCtxt, *mut c_char, *mut _xmlNode, *mut _xmlNode) -> c_int,
>;
extern "C" {
fn fwrite(ptr: *const c_void, size: usize, nmemb: usize, stream: *mut c_void) -> usize;
fn fputs(s: *const c_char, stream: *mut c_void) -> c_int;
static mut stdout: *mut c_void;
static mut stderr: *mut c_void;
}
unsafe fn push_cstr(v: &mut Vec<u8>, s: *const c_char) {
if s.is_null() {
return;
}
let len = libc::strlen(s);
v.extend_from_slice(core::slice::from_raw_parts(s as *const u8, len));
}
unsafe fn out_bytes(fp: *mut c_void, bytes: &[u8]) {
if fp.is_null() || bytes.is_empty() {
return;
}
unsafe {
fwrite(bytes.as_ptr() as *const c_void, 1, bytes.len(), fp);
}
}
unsafe fn out_cstr(fp: *mut c_void, s: *const c_char) {
if fp.is_null() || s.is_null() {
return;
}
unsafe {
fputs(s, fp);
}
}
unsafe fn shell_generic_error(arg: *const c_char, mid: &[u8], end: &[u8]) {
let mut v = Vec::new();
unsafe {
push_cstr(&mut v, arg);
}
v.extend_from_slice(mid);
v.extend_from_slice(end);
unsafe {
out_bytes(stderr, &v);
}
}
unsafe fn shell_elem_dump(fp: *mut c_void, doc: *mut _xmlDoc, node: *mut _xmlNode) -> c_int {
if fp.is_null() || node.is_null() {
return -1;
}
let buf = io::buf_create(-1);
if buf.is_null() {
return -1;
}
let ret = tree::node_dump(buf, doc, node, 0, 0);
if ret < 0 {
io::buf_free(buf);
return -1;
}
let content = io::buf_content(buf);
let len = io::buf_length(buf);
if !content.is_null() && len > 0 {
unsafe {
fwrite(content as *const c_void, 1, len as usize, fp);
}
}
io::buf_free(buf);
0
}
unsafe fn shell_html_doc_dump(fp: *mut c_void, doc: *mut _xmlDoc) -> c_int {
if fp.is_null() || doc.is_null() {
return -1;
}
let buf = io::buf_create(-1);
if buf.is_null() {
return -1;
}
let ret = crate::xml::html::doc_dump(buf, doc);
if ret < 0 {
io::buf_free(buf);
return -1;
}
let content = io::buf_content(buf);
let len = io::buf_length(buf);
if !content.is_null() && len > 0 {
unsafe {
fwrite(content as *const c_void, 1, len as usize, fp);
}
}
io::buf_free(buf);
ret
}
unsafe fn shell_html_node_dump_file(fp: *mut c_void, node: *mut _xmlNode) -> c_int {
if fp.is_null() || node.is_null() {
return -1;
}
let buf = io::buf_create(-1);
if buf.is_null() {
return -1;
}
let before = io::buf_length(buf);
crate::xml::html::serialize_node(node, buf, 0, 0);
let after = io::buf_length(buf);
if after < 0 || before < 0 {
io::buf_free(buf);
return -1;
}
let content = io::buf_content(buf);
let len = io::buf_length(buf);
if !content.is_null() && len > 0 {
unsafe {
fwrite(content as *const c_void, 1, len as usize, fp);
}
}
io::buf_free(buf);
after - before
}
unsafe fn shell_get_node_path(node: *const _xmlNode) -> *mut xmlChar {
if node.is_null() || (*node).type_ == xmlElementType::XML_NAMESPACE_DECL as c_int {
return ptr::null_mut();
}
let mut segments: Vec<Vec<u8>> = Vec::new();
let mut cur: *const _xmlNode = node;
loop {
if cur.is_null() {
break;
}
let typ = (*cur).type_;
let mut seg: Vec<u8> = Vec::new();
let mut occur: c_int = 0;
let mut generic: bool;
let next: *const _xmlNode;
if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
|| typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
{
if !segments.is_empty() {
break;
}
seg.extend_from_slice(b"/");
next = ptr::null();
} else if typ == xmlElementType::XML_ELEMENT_NODE as c_int {
generic = false;
seg.extend_from_slice(b"/");
let name = (*cur).name;
let ns = (*cur).ns;
if !name.is_null() {
if !ns.is_null() && !(*ns).prefix.is_null() {
unsafe {
push_cstr(&mut seg, (*ns).prefix as *const c_char);
}
seg.push(b':');
unsafe {
push_cstr(&mut seg, name as *const c_char);
}
} else if !ns.is_null() {
generic = true;
seg.extend_from_slice(b"*");
} else {
unsafe {
push_cstr(&mut seg, name as *const c_char);
}
}
}
next = (*cur).parent;
let mut tmp = (*cur).prev;
while !tmp.is_null() {
if (*tmp).type_ == xmlElementType::XML_ELEMENT_NODE as c_int
&& (generic || unsafe { shell_same_element_name(cur, tmp) })
{
occur += 1;
}
tmp = (*tmp).prev;
}
if occur == 0 {
let mut tmp = (*cur).next;
while !tmp.is_null() && occur == 0 {
if (*tmp).type_ == xmlElementType::XML_ELEMENT_NODE as c_int
&& (generic || unsafe { shell_same_element_name(cur, tmp) })
{
occur += 1;
}
tmp = (*tmp).next;
}
if occur != 0 {
occur = 1;
}
} else {
occur += 1;
}
} else if typ == xmlElementType::XML_COMMENT_NODE as c_int {
seg.extend_from_slice(b"/comment()");
next = (*cur).parent;
let mut tmp = (*cur).prev;
while !tmp.is_null() {
if (*tmp).type_ == xmlElementType::XML_COMMENT_NODE as c_int {
occur += 1;
}
tmp = (*tmp).prev;
}
if occur == 0 {
let mut tmp = (*cur).next;
while !tmp.is_null() && occur == 0 {
if (*tmp).type_ == xmlElementType::XML_COMMENT_NODE as c_int {
occur += 1;
}
tmp = (*tmp).next;
}
if occur != 0 {
occur = 1;
}
} else {
occur += 1;
}
} else if typ == xmlElementType::XML_TEXT_NODE as c_int
|| typ == xmlElementType::XML_CDATA_SECTION_NODE as c_int
{
seg.extend_from_slice(b"/text()");
next = (*cur).parent;
let mut tmp = (*cur).prev;
while !tmp.is_null() {
if (*tmp).type_ == xmlElementType::XML_TEXT_NODE as c_int
|| (*tmp).type_ == xmlElementType::XML_CDATA_SECTION_NODE as c_int
{
occur += 1;
}
tmp = (*tmp).prev;
}
if occur == 0 {
let mut tmp = (*cur).next;
while !tmp.is_null() {
if (*tmp).type_ == xmlElementType::XML_TEXT_NODE as c_int
|| (*tmp).type_ == xmlElementType::XML_CDATA_SECTION_NODE as c_int
{
occur = 1;
break;
}
tmp = (*tmp).next;
}
} else {
occur += 1;
}
} else if typ == xmlElementType::XML_PI_NODE as c_int {
let mut nm = Vec::new();
nm.extend_from_slice(b"processing-instruction('");
unsafe {
push_cstr(&mut nm, (*cur).name as *const c_char);
}
nm.extend_from_slice(b"')");
seg.extend_from_slice(b"/");
seg.extend_from_slice(&nm);
next = (*cur).parent;
let mut tmp = (*cur).prev;
while !tmp.is_null() {
if (*tmp).type_ == xmlElementType::XML_PI_NODE as c_int
&& unsafe { xmlStrEqual((*cur).name, (*tmp).name) != 0 }
{
occur += 1;
}
tmp = (*tmp).prev;
}
if occur == 0 {
let mut tmp = (*cur).next;
while !tmp.is_null() && occur == 0 {
if (*tmp).type_ == xmlElementType::XML_PI_NODE as c_int
&& unsafe { xmlStrEqual((*cur).name, (*tmp).name) != 0 }
{
occur += 1;
}
tmp = (*tmp).next;
}
if occur != 0 {
occur = 1;
}
} else {
occur += 1;
}
} else if typ == xmlElementType::XML_ATTRIBUTE_NODE as c_int {
seg.extend_from_slice(b"/@");
let attr = cur as *const _xmlAttr;
let name = (*attr).name;
if !name.is_null() {
if !(*attr).ns.is_null() && !(*(*attr).ns).prefix.is_null() {
unsafe {
push_cstr(&mut seg, (*(*attr).ns).prefix as *const c_char);
}
seg.push(b':');
}
unsafe {
push_cstr(&mut seg, name as *const c_char);
}
}
next = (*attr).parent;
} else {
return ptr::null_mut();
}
if occur != 0 {
seg.extend_from_slice(format!("[{}]", occur).as_bytes());
}
segments.push(seg);
if next.is_null() {
break;
}
cur = next;
}
let mut path: Vec<u8> = Vec::new();
for seg in segments.iter().rev() {
path.extend_from_slice(seg);
}
path.push(0);
let ret = xmlMallocImpl(path.len()) as *mut xmlChar;
if ret.is_null() {
return ptr::null_mut();
}
unsafe {
ptr::copy_nonoverlapping(path.as_ptr(), ret, path.len());
}
ret
}
unsafe fn shell_same_element_name(a: *const _xmlNode, b: *const _xmlNode) -> bool {
unsafe {
if xmlStrEqual((*a).name, (*b).name) == 0 {
return false;
}
let ans = (*a).ns;
let bns = (*b).ns;
if ans == bns {
return true;
}
if !ans.is_null() && !bns.is_null() {
return xmlStrEqual((*ans).prefix, (*bns).prefix) != 0;
}
false
}
}
#[no_mangle]
pub unsafe extern "C" fn xmlShellPrintXPathError(errorType: c_int, arg: *const c_char) {
let default_arg = b"Result\0";
let arg = if arg.is_null() {
default_arg.as_ptr() as *const c_char
} else {
arg
};
if errorType == xmlXPathObjectType::XPATH_UNDEFINED as c_int {
unsafe { shell_generic_error(arg, b": ", b"no such node\n") };
} else if errorType == xmlXPathObjectType::XPATH_BOOLEAN as c_int {
unsafe { shell_generic_error(arg, b" is a Boolean", b"\n") };
} else if errorType == xmlXPathObjectType::XPATH_NUMBER as c_int {
unsafe { shell_generic_error(arg, b" is a number", b"\n") };
} else if errorType == xmlXPathObjectType::XPATH_STRING as c_int {
unsafe { shell_generic_error(arg, b" is a string", b"\n") };
} else if errorType == xmlXPathObjectType::XPATH_POINT as c_int {
unsafe { shell_generic_error(arg, b" is a point", b"\n") };
} else if errorType == xmlXPathObjectType::XPATH_RANGE as c_int {
unsafe { shell_generic_error(arg, b" is a range", b"\n") };
} else if errorType == xmlXPathObjectType::XPATH_LOCATIONSET as c_int {
unsafe { shell_generic_error(arg, b" is a range", b"\n") };
} else if errorType == xmlXPathObjectType::XPATH_USERS as c_int {
unsafe { shell_generic_error(arg, b" is user-defined", b"\n") };
} else if errorType == xmlXPathObjectType::XPATH_XSLT_TREE as c_int {
unsafe { shell_generic_error(arg, b" is an XSLT value tree", b"\n") };
}
}
unsafe fn xmlShellPrintNodeCtxt(ctxt: *mut _xmlShellCtxt, node: *mut _xmlNode) {
if node.is_null() {
return;
}
let fp = if ctxt.is_null() {
unsafe { stdout }
} else {
(*ctxt).output
};
let typ = unsafe { (*node).type_ };
if typ == xmlElementType::XML_DOCUMENT_NODE as c_int {
tree::xmlDocDump(fp, node as *mut _xmlDoc);
} else if typ == xmlElementType::XML_ATTRIBUTE_NODE as c_int {
unsafe {
debug::xmlDebugDumpAttrList(fp as *mut debug::_IO_FILE, node as *mut _xmlAttr, 0);
}
} else {
unsafe {
shell_elem_dump(fp, (*node).doc, node);
}
}
unsafe {
out_bytes(fp, b"\n");
}
}
#[no_mangle]
pub unsafe extern "C" fn xmlShellPrintNode(node: *mut _xmlNode) {
unsafe { xmlShellPrintNodeCtxt(ptr::null_mut(), node) };
}
#[no_mangle]
pub unsafe extern "C" fn xmlShellPrintXPathResult(list: *mut _xmlXPathObject) {
unsafe {
xmlXPathDebugDumpObject(stdout, list, 0);
}
}
#[no_mangle]
pub unsafe extern "C" fn xmlShellList(
ctxt: *mut _xmlShellCtxt,
_arg: *mut c_char,
node: *mut _xmlNode,
_node2: *mut _xmlNode,
) -> c_int {
if ctxt.is_null() {
return 0;
}
if node.is_null() {
unsafe {
out_bytes((*ctxt).output, b"NULL\n");
}
return 0;
}
let typ = unsafe { (*node).type_ };
let mut cur: *mut _xmlNode;
if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
|| typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
{
cur = unsafe { (*(node as *mut _xmlDoc)).children };
} else if typ == xmlElementType::XML_NAMESPACE_DECL as c_int {
unsafe {
debug::xmlLsOneNode((*ctxt).output as *mut debug::_IO_FILE, node);
}
return 0;
} else if !unsafe { (*node).children }.is_null() {
cur = unsafe { (*node).children };
} else {
unsafe {
debug::xmlLsOneNode((*ctxt).output as *mut debug::_IO_FILE, node);
}
return 0;
}
while !cur.is_null() {
unsafe {
debug::xmlLsOneNode((*ctxt).output as *mut debug::_IO_FILE, cur);
cur = (*cur).next;
}
}
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlShellBase(
ctxt: *mut _xmlShellCtxt,
_arg: *mut c_char,
node: *mut _xmlNode,
_node2: *mut _xmlNode,
) -> c_int {
if ctxt.is_null() {
return 0;
}
if node.is_null() {
unsafe {
out_bytes((*ctxt).output, b"NULL\n");
}
return 0;
}
let base = unsafe { xmlNodeGetBase((*node).doc, node) };
if base.is_null() {
unsafe {
out_bytes((*ctxt).output, b" No base found !!!\n");
}
} else {
unsafe {
out_cstr((*ctxt).output, base as *const c_char);
out_bytes((*ctxt).output, b"\n");
xmlFreeImpl(base as *mut c_void);
}
}
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlShellDir(
ctxt: *mut _xmlShellCtxt,
_arg: *mut c_char,
node: *mut _xmlNode,
_node2: *mut _xmlNode,
) -> c_int {
if ctxt.is_null() {
return 0;
}
if node.is_null() {
unsafe {
out_bytes((*ctxt).output, b"NULL\n");
}
return 0;
}
let typ = unsafe { (*node).type_ };
unsafe {
if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
|| typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
{
debug::xmlDebugDumpDocumentHead(
(*ctxt).output as *mut debug::_IO_FILE,
node as *mut _xmlDoc,
);
} else if typ == xmlElementType::XML_ATTRIBUTE_NODE as c_int {
debug::xmlDebugDumpAttr(
(*ctxt).output as *mut debug::_IO_FILE,
node as *mut _xmlAttr,
0,
);
} else {
debug::xmlDebugDumpOneNode((*ctxt).output as *mut debug::_IO_FILE, node, 0);
}
}
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlShellCat(
ctxt: *mut _xmlShellCtxt,
_arg: *mut c_char,
node: *mut _xmlNode,
_node2: *mut _xmlNode,
) -> c_int {
if ctxt.is_null() {
return 0;
}
if node.is_null() {
unsafe {
out_bytes((*ctxt).output, b"NULL\n");
}
return 0;
}
let out = unsafe { (*ctxt).output };
let is_html =
unsafe { (*(*ctxt).doc).type_ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int };
let typ = unsafe { (*node).type_ };
unsafe {
if is_html {
if typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int {
shell_html_doc_dump(out, node as *mut _xmlDoc);
} else {
shell_html_node_dump_file(out, node);
}
} else if typ == xmlElementType::XML_DOCUMENT_NODE as c_int {
tree::xmlDocDump(out, node as *mut _xmlDoc);
} else {
shell_elem_dump(out, (*ctxt).doc, node);
}
out_bytes(out, b"\n");
}
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlShellLoad(
ctxt: *mut _xmlShellCtxt,
filename: *mut c_char,
_node: *mut _xmlNode,
_node2: *mut _xmlNode,
) -> c_int {
if ctxt.is_null() || filename.is_null() {
return -1;
}
let mut html = 0;
if !unsafe { (*ctxt).doc }.is_null() {
html = unsafe {
((*(*ctxt).doc).type_ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int) as c_int
};
}
let doc: *mut _xmlDoc = if html != 0 {
unsafe { crate::xml::html::parse_file(filename, ptr::null()) }
} else {
unsafe { xmlReadFile(filename, ptr::null(), 0) }
};
if !doc.is_null() {
unsafe {
if (*ctxt).loaded == 1 {
tree::free_doc((*ctxt).doc);
}
(*ctxt).loaded = 1;
xmlXPathFreeContext((*ctxt).pctxt);
if !(*ctxt).filename.is_null() {
xmlFreeImpl((*ctxt).filename as *mut c_void);
}
(*ctxt).doc = doc;
(*ctxt).node = doc as *mut _xmlNode;
(*ctxt).pctxt = xmlXPathNewContext(doc);
(*ctxt).filename = xmlCanonicPath(filename) as *mut c_char;
}
0
} else {
-1
}
}
#[no_mangle]
pub unsafe extern "C" fn xmlShellWrite(
ctxt: *mut _xmlShellCtxt,
filename: *mut c_char,
node: *mut _xmlNode,
_node2: *mut _xmlNode,
) -> c_int {
if node.is_null() {
return -1;
}
if filename.is_null() || *filename == 0 {
unsafe {
shell_generic_error(
b"Write command requires a filename argument\n\0".as_ptr() as *const c_char,
b"",
b"",
);
}
return -1;
}
if libc::access(filename, libc::W_OK) != 0 {
unsafe {
shell_generic_error(b"Cannot write to \0".as_ptr() as *const c_char, b"", b"");
shell_generic_error(filename, b"", b"\n");
}
return -1;
}
let typ = unsafe { (*node).type_ };
unsafe {
match typ {
t if t == xmlElementType::XML_DOCUMENT_NODE as c_int => {
if xmlSaveFile(filename, (*ctxt).doc) < -1 {
shell_generic_error(
b"Failed to write to \0".as_ptr() as *const c_char,
b"",
b"",
);
shell_generic_error(filename, b"", b"\n");
return -1;
}
}
t if t == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int => {
if shell_save_html_doc(filename, (*ctxt).doc) < 0 {
shell_generic_error(
b"Failed to write to \0".as_ptr() as *const c_char,
b"",
b"",
);
shell_generic_error(filename, b"", b"\n");
return -1;
}
}
_ => {
let f = libc::fopen(filename, b"w\0".as_ptr() as *const c_char);
if f.is_null() {
shell_generic_error(
b"Failed to write to \0".as_ptr() as *const c_char,
b"",
b"",
);
shell_generic_error(filename, b"", b"\n");
return -1;
}
shell_elem_dump(f as *mut c_void, (*ctxt).doc, node);
libc::fclose(f);
}
}
}
0
}
unsafe fn shell_save_html_doc(filename: *const c_char, doc: *mut _xmlDoc) -> c_int {
if filename.is_null() || doc.is_null() {
return -1;
}
let buf = io::buf_create(-1);
if buf.is_null() {
return -1;
}
let ret = crate::xml::html::doc_dump(buf, doc);
if ret < 0 {
io::buf_free(buf);
return -1;
}
let content = io::buf_content(buf);
let len = io::buf_length(buf);
let written = if !content.is_null() && len > 0 {
let f = libc::fopen(filename, b"w\0".as_ptr() as *const c_char);
if f.is_null() {
io::buf_free(buf);
return -1;
}
let n = libc::fwrite(content as *const c_void, 1, len as usize, f);
libc::fclose(f);
n as c_int
} else {
0
};
io::buf_free(buf);
written
}
#[no_mangle]
pub unsafe extern "C" fn xmlShellSave(
ctxt: *mut _xmlShellCtxt,
filename: *mut c_char,
_node: *mut _xmlNode,
_node2: *mut _xmlNode,
) -> c_int {
if ctxt.is_null() || unsafe { (*ctxt).doc }.is_null() {
return -1;
}
let mut filename = filename;
if filename.is_null() || *filename == 0 {
filename = unsafe { (*ctxt).filename };
}
if filename.is_null() {
return -1;
}
if libc::access(filename, libc::W_OK) != 0 {
unsafe {
shell_generic_error(b"Cannot save to \0".as_ptr() as *const c_char, b"", b"");
shell_generic_error(filename, b"", b"\n");
}
return -1;
}
let typ = unsafe { (*(*ctxt).doc).type_ };
unsafe {
match typ {
t if t == xmlElementType::XML_DOCUMENT_NODE as c_int => {
if xmlSaveFile(filename, (*ctxt).doc) < 0 {
shell_generic_error(
b"Failed to save to \0".as_ptr() as *const c_char,
b"",
b"",
);
shell_generic_error(filename, b"", b"\n");
}
}
t if t == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int => {
if shell_save_html_doc(filename, (*ctxt).doc) < 0 {
shell_generic_error(
b"Failed to save to \0".as_ptr() as *const c_char,
b"",
b"",
);
shell_generic_error(filename, b"", b"\n");
}
}
_ => {
shell_generic_error(
b"To save to subparts of a document use the 'write' command\n\0".as_ptr()
as *const c_char,
b"",
b"",
);
return -1;
}
}
}
0
}
unsafe extern "C" fn shell_valid_error(_ctx: *mut c_void, msg: *const c_char) {
unsafe {
if !msg.is_null() {
out_cstr(stderr, msg);
}
}
}
unsafe fn shell_parse_dtd(dtd: *const c_char) -> *mut _xmlDtd {
if dtd.is_null() {
return ptr::null_mut();
}
let path = match unsafe { core::ffi::CStr::from_ptr(dtd) }.to_str() {
Ok(p) => p,
Err(_) => return ptr::null_mut(),
};
let content = match std::fs::read(path) {
Ok(c) => c,
Err(_) => return ptr::null_mut(),
};
let lower: Vec<u8> = content.iter().map(|b| b.to_ascii_lowercase()).collect();
let pos = match find_subslice(&lower, b"<!doctype") {
Some(p) => p,
None => {
let base = path.rsplit(['/', '\\']).next().unwrap_or(path);
let name_c = bytes_to_xmlstr(base.as_bytes());
let dtd_node =
crate::xml::dtd::new_dtd(ptr::null_mut(), name_c, ptr::null_mut(), ptr::null_mut());
if !name_c.is_null() {
xmlFreeImpl(name_c as *mut c_void);
}
return dtd_node;
}
};
let mut i = pos + b"<!doctype".len();
while i < content.len() && (content[i] as char).is_ascii_whitespace() {
i += 1;
}
let name_start = i;
while i < content.len()
&& !(content[i] as char).is_ascii_whitespace()
&& content[i] != b'>'
&& content[i] != b'['
{
i += 1;
}
if i == name_start {
return ptr::null_mut();
}
let name = &content[name_start..i];
while i < content.len() && (content[i] as char).is_ascii_whitespace() {
i += 1;
}
let mut public_id: Option<&[u8]> = None;
let mut system_id: Option<&[u8]> = None;
if i < content.len() && content[i..].to_ascii_lowercase().starts_with(b"public") {
i += b"public".len();
while i < content.len() && (content[i] as char).is_ascii_whitespace() {
i += 1;
}
if i < content.len() && content[i] == b'"' {
let s = i + 1;
let e = content[s..].iter().position(|&c| c == b'"').map(|p| s + p);
if let Some(e) = e {
public_id = Some(&content[s..e]);
}
}
while i < content.len() && (content[i] as char).is_ascii_whitespace() {
i += 1;
}
if i < content.len() && content[i] == b'"' {
let s = i + 1;
let e = content[s..].iter().position(|&c| c == b'"').map(|p| s + p);
if let Some(e) = e {
system_id = Some(&content[s..e]);
}
}
} else if i < content.len() && content[i..].to_ascii_lowercase().starts_with(b"system") {
i += b"system".len();
while i < content.len() && (content[i] as char).is_ascii_whitespace() {
i += 1;
}
if i < content.len() && content[i] == b'"' {
let s = i + 1;
let e = content[s..].iter().position(|&c| c == b'"').map(|p| s + p);
if let Some(e) = e {
system_id = Some(&content[s..e]);
}
}
}
let name_c = bytes_to_xmlstr(name);
let ext_c = match public_id {
Some(v) => bytes_to_xmlstr(v),
None => ptr::null_mut(),
};
let sys_c = match system_id {
Some(v) => bytes_to_xmlstr(v),
None => ptr::null_mut(),
};
let dtd_node = crate::xml::dtd::new_dtd(ptr::null_mut(), name_c, ext_c, sys_c);
if !name_c.is_null() {
xmlFreeImpl(name_c as *mut c_void);
}
if !ext_c.is_null() {
xmlFreeImpl(ext_c as *mut c_void);
}
if !sys_c.is_null() {
xmlFreeImpl(sys_c as *mut c_void);
}
dtd_node
}
fn find_subslice(haystack: &[u8], needle: &[u8]) -> Option<usize> {
if needle.is_empty() || haystack.len() < needle.len() {
return None;
}
haystack.windows(needle.len()).position(|w| w == needle)
}
unsafe fn bytes_to_xmlstr(bytes: &[u8]) -> *mut xmlChar {
let buf = xmlMallocImpl(bytes.len() + 1) as *mut xmlChar;
if buf.is_null() {
return ptr::null_mut();
}
unsafe {
ptr::copy_nonoverlapping(bytes.as_ptr(), buf, bytes.len());
*buf.add(bytes.len()) = 0;
}
buf
}
#[no_mangle]
pub unsafe extern "C" fn xmlShellValidate(
ctxt: *mut _xmlShellCtxt,
dtd: *mut c_char,
_node: *mut _xmlNode,
_node2: *mut _xmlNode,
) -> c_int {
if ctxt.is_null() || unsafe { (*ctxt).doc }.is_null() {
return -1;
}
let vctxt = xmlMallocZero(size_of::<_xmlValidCtxt>()) as *mut _xmlValidCtxt;
if vctxt.is_null() {
return -1;
}
unsafe {
(*vctxt).error = Some(shell_valid_error);
(*vctxt).warning = Some(shell_valid_error);
}
let mut res = -1;
unsafe {
if dtd.is_null() || *dtd == 0 {
res = xmlValidateDocument(vctxt, (*ctxt).doc);
} else {
let subset = shell_parse_dtd(dtd as *const c_char);
if !subset.is_null() {
res = xmlValidateDtd(vctxt, (*ctxt).doc, subset);
crate::xml::dtd::free_dtd(subset);
}
}
}
unsafe {
xmlFreeImpl(vctxt as *mut c_void);
}
res
}
#[no_mangle]
pub unsafe extern "C" fn xmlShellDu(
ctxt: *mut _xmlShellCtxt,
_arg: *mut c_char,
tree: *mut _xmlNode,
_node2: *mut _xmlNode,
) -> c_int {
if ctxt.is_null() {
return -1;
}
if tree.is_null() {
return -1;
}
let out = unsafe { (*ctxt).output };
let mut indent: c_int = 0;
let mut node: *mut _xmlNode = tree;
unsafe {
while !node.is_null() {
let typ = (*node).type_;
if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
|| typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
{
out_bytes(out, b"/\n");
} else if typ == xmlElementType::XML_ELEMENT_NODE as c_int {
let mut line = Vec::new();
for _ in 0..indent {
line.extend_from_slice(b" ");
}
if !(*node).ns.is_null() && !(*(*node).ns).prefix.is_null() {
push_cstr(&mut line, (*(*node).ns).prefix as *const c_char);
line.push(b':');
}
push_cstr(&mut line, (*node).name as *const c_char);
line.push(b'\n');
out_bytes(out, &line);
}
if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
|| typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
{
node = (*(node as *mut _xmlDoc)).children;
} else if !(*node).children.is_null()
&& typ != xmlElementType::XML_ENTITY_REF_NODE as c_int
{
node = (*node).children;
indent += 1;
} else if node != tree && !(*node).next.is_null() {
node = (*node).next;
} else if node != tree {
while node != tree {
if !(*node).parent.is_null() {
node = (*node).parent;
indent -= 1;
}
if node != tree && !(*node).next.is_null() {
node = (*node).next;
break;
}
if (*node).parent.is_null() {
node = ptr::null_mut();
break;
}
if node == tree {
node = ptr::null_mut();
break;
}
}
if node == tree {
node = ptr::null_mut();
}
} else {
node = ptr::null_mut();
}
}
}
0
}
#[no_mangle]
pub unsafe extern "C" fn xmlShellPwd(
_ctxt: *mut _xmlShellCtxt,
buffer: *mut c_char,
node: *mut _xmlNode,
_node2: *mut _xmlNode,
) -> c_int {
if node.is_null() || buffer.is_null() {
return -1;
}
let path = unsafe { shell_get_node_path(node) };
if path.is_null() {
return -1;
}
let plen = unsafe { tree::xml_strlen(path) } as usize;
let n = plen.min(498);
unsafe {
ptr::copy_nonoverlapping(path as *const u8, buffer as *mut u8, n);
*buffer.add(n) = 0;
*buffer.add(499) = b'0' as c_char;
}
unsafe {
xmlFreeImpl(path as *mut c_void);
}
0
}
unsafe fn xmlShellSetBase(ctxt: *mut _xmlShellCtxt, arg: *mut c_char, node: *mut _xmlNode) {
let _ = ctxt;
if !node.is_null() {
unsafe {
xmlNodeSetBase(node, arg as *const xmlChar);
}
}
}
unsafe fn xmlShellRegisterNamespace(ctxt: *mut _xmlShellCtxt, arg: *mut c_char) -> c_int {
let ns_list_dup = unsafe { xmlStrdup(arg as *const xmlChar) };
if ns_list_dup.is_null() {
return -1;
}
let mut next: *mut xmlChar = ns_list_dup;
loop {
if unsafe { *next == 0 } {
break;
}
let prefix = next;
let eq = unsafe { xmlStrchr(next, b'=' as xmlChar) };
if eq.is_null() {
unsafe {
out_cstr(
(*ctxt).output,
b"setns: prefix=[nsuri] required\n\0".as_ptr() as *const c_char,
);
}
unsafe {
xmlFreeImpl(ns_list_dup as *mut c_void);
}
return -1;
}
unsafe {
*(eq as *mut xmlChar) = 0;
}
let href = unsafe { eq.add(1) };
let space = unsafe { xmlStrchr(href, b' ' as xmlChar) };
if !space.is_null() {
unsafe {
*(space as *mut xmlChar) = 0;
}
next = unsafe { space.add(1) as *mut xmlChar };
} else {
next = unsafe { href.add(tree::xml_strlen(href) as usize) as *mut xmlChar };
}
if unsafe { xmlXPathRegisterNs((*ctxt).pctxt, prefix, href) } != 0 {
unsafe {
let mut msg = Vec::new();
msg.extend_from_slice(b"Error: unable to register NS with prefix=\"");
push_cstr(&mut msg, prefix as *const c_char);
msg.extend_from_slice(b"\" and href=\"");
push_cstr(&mut msg, href as *const c_char);
msg.extend_from_slice(b"\"\n");
out_bytes((*ctxt).output, &msg);
}
unsafe {
xmlFreeImpl(ns_list_dup as *mut c_void);
}
return -1;
}
}
unsafe {
xmlFreeImpl(ns_list_dup as *mut c_void);
}
0
}
unsafe fn xmlShellRegisterRootNamespaces(ctxt: *mut _xmlShellCtxt, root: *mut _xmlNode) -> c_int {
if root.is_null()
|| unsafe { (*root).type_ != xmlElementType::XML_ELEMENT_NODE as c_int }
|| unsafe { (*root).nsDef.is_null() }
|| ctxt.is_null()
|| unsafe { (*ctxt).pctxt.is_null() }
{
return -1;
}
let mut ns = unsafe { (*root).nsDef };
while !ns.is_null() {
if unsafe { (*ns).prefix.is_null() } {
unsafe {
xmlXPathRegisterNs(
(*ctxt).pctxt,
b"defaultns\0".as_ptr() as *const xmlChar,
(*ns).href,
);
}
} else {
unsafe {
xmlXPathRegisterNs((*ctxt).pctxt, (*ns).prefix, (*ns).href);
}
}
ns = unsafe { (*ns).next };
}
0
}
unsafe fn xmlShellGrep(ctxt: *mut _xmlShellCtxt, arg: *mut c_char, node: *mut _xmlNode) {
if ctxt.is_null() || node.is_null() || arg.is_null() {
return;
}
let mut node = node;
while !node.is_null() {
unsafe {
let typ = (*node).type_;
if typ == xmlElementType::XML_COMMENT_NODE as c_int {
if !xmlStrstr((*node).content, arg as *const xmlChar).is_null() {
let path = shell_get_node_path(node);
if !path.is_null() {
let mut line = Vec::new();
push_cstr(&mut line, path as *const c_char);
line.extend_from_slice(b" : ");
out_bytes((*ctxt).output, &line);
xmlFreeImpl(path as *mut c_void);
}
xmlShellList(ctxt, ptr::null_mut(), node, ptr::null_mut());
}
} else if typ == xmlElementType::XML_TEXT_NODE as c_int {
if !xmlStrstr((*node).content, arg as *const xmlChar).is_null() {
let path = shell_get_node_path((*node).parent);
if !path.is_null() {
let mut line = Vec::new();
push_cstr(&mut line, path as *const c_char);
line.extend_from_slice(b" : ");
out_bytes((*ctxt).output, &line);
xmlFreeImpl(path as *mut c_void);
}
xmlShellList(ctxt, ptr::null_mut(), (*node).parent, ptr::null_mut());
}
}
if typ == xmlElementType::XML_DOCUMENT_NODE as c_int
|| typ == xmlElementType::XML_HTML_DOCUMENT_NODE as c_int
{
node = (*(node as *mut _xmlDoc)).children;
} else if !(*node).children.is_null()
&& typ != xmlElementType::XML_ENTITY_REF_NODE as c_int
{
node = (*node).children;
} else if !(*node).next.is_null() {
node = (*node).next;
} else {
while !node.is_null() {
if !(*node).parent.is_null() {
node = (*node).parent;
}
if !(*node).next.is_null() {
node = (*node).next;
break;
}
if (*node).parent.is_null() {
node = ptr::null_mut();
break;
}
}
}
}
}
}
unsafe fn shell_result_type_error(arg: *const c_char, typ: c_int) {
if typ == xmlXPathObjectType::XPATH_UNDEFINED as c_int {
unsafe { shell_generic_error(arg, b": ", b"no such node\n") };
} else if typ == xmlXPathObjectType::XPATH_BOOLEAN as c_int {
unsafe { shell_generic_error(arg, b" is a Boolean", b"\n") };
} else if typ == xmlXPathObjectType::XPATH_NUMBER as c_int {
unsafe { shell_generic_error(arg, b" is a number", b"\n") };
} else if typ == xmlXPathObjectType::XPATH_STRING as c_int {
unsafe { shell_generic_error(arg, b" is a string", b"\n") };
} else if typ == xmlXPathObjectType::XPATH_POINT as c_int {
unsafe { shell_generic_error(arg, b" is a point", b"\n") };
} else if typ == xmlXPathObjectType::XPATH_RANGE as c_int {
unsafe { shell_generic_error(arg, b" is a range", b"\n") };
} else if typ == xmlXPathObjectType::XPATH_LOCATIONSET as c_int {
unsafe { shell_generic_error(arg, b" is a range", b"\n") };
} else if typ == xmlXPathObjectType::XPATH_USERS as c_int {
unsafe { shell_generic_error(arg, b" is user-defined", b"\n") };
} else if typ == xmlXPathObjectType::XPATH_XSLT_TREE as c_int {
unsafe { shell_generic_error(arg, b" is an XSLT value tree", b"\n") };
}
}
unsafe fn shell_build_prompt(ctxt: *mut _xmlShellCtxt) -> Vec<u8> {
let mut p = Vec::new();
let node = unsafe { (*ctxt).node };
let doc = unsafe { (*ctxt).doc };
if node == doc as *mut _xmlNode {
p.extend_from_slice(b"/ > ");
} else if !node.is_null() && !unsafe { (*node).name }.is_null() {
unsafe {
if !(*node).ns.is_null() && !(*(*node).ns).prefix.is_null() {
push_cstr(&mut p, (*(*node).ns).prefix as *const c_char);
p.push(b':');
}
push_cstr(&mut p, (*node).name as *const c_char);
}
p.extend_from_slice(b" > ");
} else {
p.extend_from_slice(b"? > ");
}
p.push(0);
p
}
unsafe fn shell_print_help(ctxt: *mut _xmlShellCtxt) {
let out = unsafe { (*ctxt).output };
const HELP: &[&[u8]] = &[
b"\tbase display XML base of the node\n",
b"\tsetbase URI change the XML base of the node\n",
b"\tbye leave shell\n",
b"\tcat [node] display node or current node\n",
b"\tcd [path] change directory to path or to root\n",
b"\tdir [path] dumps information about the node (namespace, attributes, content)\n",
b"\tdu [path] show the structure of the subtree under path or the current node\n",
b"\texit leave shell\n",
b"\thelp display this help\n",
b"\tfree display memory usage\n",
b"\tload [name] load a new document with name\n",
b"\tls [path] list contents of path or the current directory\n",
b"\txpath expr evaluate the XPath expression in that context and print the result\n",
b"\tsetns nsreg register a namespace to a prefix in the XPath evaluation context\n",
b"\t format for nsreg is: prefix=[nsuri] (i.e. prefix= unsets a prefix)\n",
b"\tsetrootns register all namespace found on the root element\n",
b"\t the default namespace if any uses 'defaultns' prefix\n",
b"\tpwd display current working directory\n",
b"\twhereis display absolute path of [path] or current working directory\n",
b"\tquit leave shell\n",
b"\tsave [name] save this document to name or the original name\n",
b"\twrite [name] write the current node to the filename\n",
b"\tvalidate check the document for errors\n",
b"\tgrep string search for a string in the subtree\n",
];
for line in HELP {
unsafe {
out_bytes(out, line);
}
}
}
#[no_mangle]
pub unsafe extern "C" fn xmlShell(
doc: *mut _xmlDoc,
filename: *mut c_char,
input: xmlShellReadlineFunc,
output: *mut c_void,
) {
if doc.is_null() || filename.is_null() || input.is_none() {
return;
}
let output = if output.is_null() {
unsafe { stdout }
} else {
output
};
let ctxt = xmlMallocZero(size_of::<_xmlShellCtxt>()) as *mut _xmlShellCtxt;
if ctxt.is_null() {
return;
}
unsafe {
(*ctxt).loaded = 0;
(*ctxt).doc = doc;
(*ctxt).input = input;
(*ctxt).output = output;
(*ctxt).filename = xmlStrdup(filename as *const xmlChar) as *mut c_char;
(*ctxt).node = doc as *mut _xmlNode;
(*ctxt).pctxt = xmlXPathNewContext(doc);
}
if unsafe { (*ctxt).pctxt }.is_null() {
unsafe {
xmlFreeImpl(ctxt as *mut c_void);
}
return;
}
let mut cmdline: *mut c_char = ptr::null_mut();
loop {
let prompt = unsafe { shell_build_prompt(ctxt) };
let readline = unsafe { (*ctxt).input };
cmdline = match readline {
Some(f) => f(prompt.as_ptr() as *mut c_char),
None => break,
};
if cmdline.is_null() {
break;
}
let clen = unsafe { libc::strlen(cmdline) } as usize;
let cbytes = unsafe { core::slice::from_raw_parts(cmdline as *const u8, clen) };
let mut i = 0usize;
while i < clen && (cbytes[i] == b' ' || cbytes[i] == b'\t') {
i += 1;
}
let mut command: Vec<u8> = Vec::new();
while i < clen
&& cbytes[i] != b' '
&& cbytes[i] != b'\t'
&& cbytes[i] != b'\n'
&& cbytes[i] != b'\r'
{
command.push(cbytes[i]);
i += 1;
}
if command.is_empty() {
unsafe {
libc::free(cmdline as *mut c_void);
}
cmdline = ptr::null_mut();
continue;
}
while i < clen && (cbytes[i] == b' ' || cbytes[i] == b'\t') {
i += 1;
}
let mut arg: Vec<u8> = Vec::new();
while i < clen && cbytes[i] != b'\n' && cbytes[i] != b'\r' {
arg.push(cbytes[i]);
i += 1;
}
command.push(0);
let cmd: &[u8] = &command;
let mut argn = arg.clone();
argn.push(0);
let arg_cstr: *mut c_char = argn.as_mut_ptr() as *mut c_char;
let arg_xml: *const xmlChar = argn.as_ptr() as *const xmlChar;
if cmd == b"exit\0" || cmd == b"quit\0" || cmd == b"bye\0" {
break;
}
if cmd == b"help\0" {
unsafe { shell_print_help(ctxt) };
} else if cmd == b"validate\0" {
unsafe {
xmlShellValidate(ctxt, arg_cstr, ptr::null_mut(), ptr::null_mut());
}
} else if cmd == b"load\0" {
unsafe {
xmlShellLoad(ctxt, arg_cstr, ptr::null_mut(), ptr::null_mut());
}
} else if cmd == b"save\0" {
unsafe {
xmlShellSave(ctxt, arg_cstr, ptr::null_mut(), ptr::null_mut());
}
} else if cmd == b"write\0" {
if arg.is_empty() {
unsafe {
shell_generic_error(
b"Write command requires a filename argument\n\0".as_ptr() as *const c_char,
b"",
b"",
);
}
} else {
unsafe {
xmlShellWrite(ctxt, arg_cstr, (*ctxt).node, ptr::null_mut());
}
}
} else if cmd == b"grep\0" {
unsafe {
xmlShellGrep(ctxt, arg_cstr, (*ctxt).node);
}
} else if cmd == b"free\0" {
unsafe {
if arg.is_empty() {
crate::abi::allocator::xmlMemShow((*ctxt).output, 0);
} else {
let mut len: c_int = 0;
let arg_s = core::str::from_utf8(&argn[..argn.len() - 1]).unwrap_or("");
if let Ok(v) = arg_s.trim().parse::<c_int>() {
len = v;
}
crate::abi::allocator::xmlMemShow((*ctxt).output, len);
}
}
} else if cmd == b"pwd\0" {
let mut dir = [0 as c_char; 500];
unsafe {
if xmlShellPwd(ctxt, dir.as_mut_ptr(), (*ctxt).node, ptr::null_mut()) == 0 {
let mut line = Vec::new();
push_cstr(&mut line, dir.as_mut_ptr());
line.extend_from_slice(b"\n");
out_bytes((*ctxt).output, &line);
}
}
} else if cmd == b"du\0" {
unsafe {
if arg.is_empty() {
xmlShellDu(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
} else {
(*(*ctxt).pctxt).node = (*ctxt).node;
let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
if !list.is_null() {
let typ = (*list).type_;
if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
let ns = (*list).nodesetval as *mut _xmlNodeSet;
if !ns.is_null() {
for indx in 0..(*ns).nodeNr {
let n = *(*ns).nodeTab.add(indx as usize);
xmlShellDu(ctxt, ptr::null_mut(), n, ptr::null_mut());
}
}
} else {
shell_result_type_error(arg_cstr, typ);
}
xmlXPathFreeObject(list);
} else {
shell_generic_error(arg_cstr, b": ", b"no such node\n");
}
(*(*ctxt).pctxt).node = ptr::null_mut();
}
}
} else if cmd == b"base\0" {
unsafe {
xmlShellBase(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
}
} else if cmd == b"setns\0" {
unsafe {
if arg.is_empty() {
shell_generic_error(
b"setns: prefix=[nsuri] required\n\0".as_ptr() as *const c_char,
b"",
b"",
);
} else {
xmlShellRegisterNamespace(ctxt, arg_cstr);
}
}
} else if cmd == b"setrootns\0" {
unsafe {
let root = tree::doc_get_root_element((*ctxt).doc);
xmlShellRegisterRootNamespaces(ctxt, root);
}
} else if cmd == b"xpath\0" {
unsafe {
if arg.is_empty() {
shell_generic_error(
b"xpath: expression required\n\0".as_ptr() as *const c_char,
b"",
b"",
);
} else {
(*(*ctxt).pctxt).node = (*ctxt).node;
let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
xmlXPathDebugDumpObject((*ctxt).output, list, 0);
xmlXPathFreeObject(list);
}
}
} else if cmd == b"setbase\0" {
unsafe {
xmlShellSetBase(ctxt, arg_cstr, (*ctxt).node);
}
} else if cmd == b"ls\0" || cmd == b"dir\0" {
let is_dir = cmd == b"dir\0";
unsafe {
if arg.is_empty() {
if is_dir {
xmlShellDir(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
} else {
xmlShellList(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
}
} else {
(*(*ctxt).pctxt).node = (*ctxt).node;
let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
if !list.is_null() {
let typ = (*list).type_;
if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
let ns = (*list).nodesetval as *mut _xmlNodeSet;
if !ns.is_null() {
for indx in 0..(*ns).nodeNr {
let n = *(*ns).nodeTab.add(indx as usize);
if is_dir {
xmlShellDir(ctxt, ptr::null_mut(), n, ptr::null_mut());
} else {
xmlShellList(ctxt, ptr::null_mut(), n, ptr::null_mut());
}
}
}
} else {
shell_result_type_error(arg_cstr, typ);
}
xmlXPathFreeObject(list);
} else {
shell_generic_error(arg_cstr, b": ", b"no such node\n");
}
(*(*ctxt).pctxt).node = ptr::null_mut();
}
}
} else if cmd == b"whereis\0" {
let mut dir = [0 as c_char; 500];
unsafe {
if arg.is_empty() {
if xmlShellPwd(ctxt, dir.as_mut_ptr(), (*ctxt).node, ptr::null_mut()) == 0 {
let mut line = Vec::new();
push_cstr(&mut line, dir.as_mut_ptr());
line.extend_from_slice(b"\n");
out_bytes((*ctxt).output, &line);
}
} else {
(*(*ctxt).pctxt).node = (*ctxt).node;
let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
if !list.is_null() {
let typ = (*list).type_;
if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
let ns = (*list).nodesetval as *mut _xmlNodeSet;
if !ns.is_null() {
for indx in 0..(*ns).nodeNr {
let n = *(*ns).nodeTab.add(indx as usize);
if xmlShellPwd(ctxt, dir.as_mut_ptr(), n, ptr::null_mut()) == 0
{
let mut line = Vec::new();
push_cstr(&mut line, dir.as_mut_ptr());
line.extend_from_slice(b"\n");
out_bytes((*ctxt).output, &line);
}
}
}
} else {
shell_result_type_error(arg_cstr, typ);
}
xmlXPathFreeObject(list);
} else {
shell_generic_error(arg_cstr, b": ", b"no such node\n");
}
(*(*ctxt).pctxt).node = ptr::null_mut();
}
}
} else if cmd == b"cd\0" {
unsafe {
if arg.is_empty() {
(*ctxt).node = (*ctxt).doc as *mut _xmlNode;
} else {
let mut argn = argn;
let l = argn.len();
if l >= 3 && argn[l - 2] == b'/' {
argn[l - 2] = 0;
}
(*(*ctxt).pctxt).node = (*ctxt).node;
let list = xmlXPathEval(argn.as_ptr() as *const xmlChar, (*ctxt).pctxt);
if !list.is_null() {
let typ = (*list).type_;
if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
let ns = (*list).nodesetval as *mut _xmlNodeSet;
if !ns.is_null() {
if (*ns).nodeNr == 1 {
(*ctxt).node = *(*ns).nodeTab;
if !(*ctxt).node.is_null()
&& (*(*ctxt).node).type_
== xmlElementType::XML_NAMESPACE_DECL as c_int
{
shell_generic_error(
b"cannot cd to namespace\n\0".as_ptr() as *const c_char,
b"",
b"",
);
(*ctxt).node = ptr::null_mut();
}
} else {
let mut msg = Vec::new();
push_cstr(&mut msg, arg_cstr);
msg.extend_from_slice(b" is a ");
msg.extend_from_slice((*ns).nodeNr.to_string().as_bytes());
msg.extend_from_slice(b" Node Set\n");
out_bytes(stderr, &msg);
}
} else {
let mut msg = Vec::new();
push_cstr(&mut msg, arg_cstr);
msg.extend_from_slice(b" is an empty Node Set\n");
out_bytes(stderr, &msg);
}
} else {
shell_result_type_error(arg_cstr, typ);
}
xmlXPathFreeObject(list);
} else {
shell_generic_error(arg_cstr, b": ", b"no such node\n");
}
(*(*ctxt).pctxt).node = ptr::null_mut();
}
}
} else if cmd == b"cat\0" {
unsafe {
if arg.is_empty() {
xmlShellCat(ctxt, ptr::null_mut(), (*ctxt).node, ptr::null_mut());
} else {
(*(*ctxt).pctxt).node = (*ctxt).node;
let list = xmlXPathEval(arg_xml, (*ctxt).pctxt);
if !list.is_null() {
let typ = (*list).type_;
if typ == xmlXPathObjectType::XPATH_NODESET as c_int {
let ns = (*list).nodesetval as *mut _xmlNodeSet;
if !ns.is_null() {
for indx in 0..(*ns).nodeNr {
if i > 0 {
out_bytes((*ctxt).output, b" -------\n");
}
let n = *(*ns).nodeTab.add(indx as usize);
xmlShellCat(ctxt, ptr::null_mut(), n, ptr::null_mut());
}
}
} else {
shell_result_type_error(arg_cstr, typ);
}
xmlXPathFreeObject(list);
} else {
shell_generic_error(arg_cstr, b": ", b"no such node\n");
}
(*(*ctxt).pctxt).node = ptr::null_mut();
}
}
} else {
let mut msg = Vec::new();
msg.extend_from_slice(b"Unknown command ");
msg.extend_from_slice(&command[..command.len() - 1]);
msg.extend_from_slice(b"\n");
unsafe {
out_bytes(stderr, &msg);
}
}
unsafe {
libc::free(cmdline as *mut c_void);
}
cmdline = ptr::null_mut();
}
unsafe {
xmlXPathFreeContext((*ctxt).pctxt);
if (*ctxt).loaded != 0 {
tree::free_doc((*ctxt).doc);
}
if !(*ctxt).filename.is_null() {
xmlFreeImpl((*ctxt).filename as *mut c_void);
}
xmlFreeImpl(ctxt as *mut c_void);
if !cmdline.is_null() {
libc::free(cmdline as *mut c_void);
}
}
}