blob: 0c51b9c0eafb33c339083b9103bb5fc90d556c15 [file] [log] [blame]
#![warn(clippy::pedantic)]
#![allow(
clippy::cast_lossless,
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_sign_loss,
clippy::items_after_statements,
clippy::let_underscore_untyped,
clippy::missing_errors_doc,
clippy::missing_safety_doc,
clippy::ptr_as_ptr,
clippy::single_match_else,
clippy::too_many_lines,
clippy::unreadable_literal
)]
mod cstr;
use self::cstr::CStr;
use std::env;
use std::error::Error;
use std::ffi::c_void;
use std::fs::File;
use std::io::{self, Read, Write};
use std::mem::MaybeUninit;
use std::process::{self, ExitCode};
use std::ptr::{self, addr_of_mut};
use std::slice;
use unsafe_libyaml::{
yaml_alias_event_initialize, yaml_document_end_event_initialize,
yaml_document_start_event_initialize, yaml_emitter_delete, yaml_emitter_emit,
yaml_emitter_initialize, yaml_emitter_set_canonical, yaml_emitter_set_output,
yaml_emitter_set_unicode, yaml_emitter_t, yaml_event_t, yaml_mapping_end_event_initialize,
yaml_mapping_start_event_initialize, yaml_scalar_event_initialize, yaml_scalar_style_t,
yaml_sequence_end_event_initialize, yaml_sequence_start_event_initialize,
yaml_stream_end_event_initialize, yaml_stream_start_event_initialize, yaml_tag_directive_t,
yaml_version_directive_t, YAML_ANY_SCALAR_STYLE, YAML_BLOCK_MAPPING_STYLE,
YAML_BLOCK_SEQUENCE_STYLE, YAML_DOUBLE_QUOTED_SCALAR_STYLE, YAML_EMITTER_ERROR,
YAML_FOLDED_SCALAR_STYLE, YAML_LITERAL_SCALAR_STYLE, YAML_MEMORY_ERROR,
YAML_PLAIN_SCALAR_STYLE, YAML_SINGLE_QUOTED_SCALAR_STYLE, YAML_UTF8_ENCODING,
YAML_WRITER_ERROR,
};
pub(crate) unsafe fn unsafe_main(
stdin: &mut dyn Read,
mut stdout: &mut dyn Write,
) -> Result<(), Box<dyn Error>> {
let mut emitter = MaybeUninit::<yaml_emitter_t>::uninit();
let emitter = emitter.as_mut_ptr();
if yaml_emitter_initialize(emitter).fail {
return Err("Could not initalize the emitter object".into());
}
unsafe fn write_to_stdio(data: *mut c_void, buffer: *mut u8, size: u64) -> i32 {
let stdout: *mut &mut dyn Write = data.cast();
let bytes = slice::from_raw_parts(buffer.cast(), size as usize);
match (*stdout).write(bytes) {
Ok(n) => n as i32,
Err(_) => 0,
}
}
yaml_emitter_set_output(emitter, write_to_stdio, addr_of_mut!(stdout).cast());
yaml_emitter_set_canonical(emitter, false);
yaml_emitter_set_unicode(emitter, false);
let mut buf = ReadBuf::new();
let mut event = MaybeUninit::<yaml_event_t>::uninit();
let event = event.as_mut_ptr();
let result = loop {
let line = match buf.get_line(stdin) {
Some(line) => line,
None => break Ok(()),
};
let mut anchor = [0u8; 256];
let mut tag = [0u8; 256];
let result = if line.starts_with(b"+STR") {
yaml_stream_start_event_initialize(event, YAML_UTF8_ENCODING)
} else if line.starts_with(b"-STR") {
yaml_stream_end_event_initialize(event)
} else if line.starts_with(b"+DOC") {
let implicit = !line[4..].starts_with(b" ---");
yaml_document_start_event_initialize(
event,
ptr::null_mut::<yaml_version_directive_t>(),
ptr::null_mut::<yaml_tag_directive_t>(),
ptr::null_mut::<yaml_tag_directive_t>(),
implicit,
)
} else if line.starts_with(b"-DOC") {
let implicit = !line[4..].starts_with(b" ...");
yaml_document_end_event_initialize(event, implicit)
} else if line.starts_with(b"+MAP") {
yaml_mapping_start_event_initialize(
event,
get_anchor(b'&', line, anchor.as_mut_ptr()),
get_tag(line, tag.as_mut_ptr()),
false,
YAML_BLOCK_MAPPING_STYLE,
)
} else if line.starts_with(b"-MAP") {
yaml_mapping_end_event_initialize(event)
} else if line.starts_with(b"+SEQ") {
yaml_sequence_start_event_initialize(
event,
get_anchor(b'&', line, anchor.as_mut_ptr()),
get_tag(line, tag.as_mut_ptr()),
false,
YAML_BLOCK_SEQUENCE_STYLE,
)
} else if line.starts_with(b"-SEQ") {
yaml_sequence_end_event_initialize(event)
} else if line.starts_with(b"=VAL") {
let mut value = [0i8; 1024];
let mut style = YAML_ANY_SCALAR_STYLE;
get_value(line, value.as_mut_ptr(), &mut style);
let implicit = get_tag(line, tag.as_mut_ptr()).is_null();
yaml_scalar_event_initialize(
event,
get_anchor(b'&', line, anchor.as_mut_ptr()),
get_tag(line, tag.as_mut_ptr()),
value.as_mut_ptr() as *mut u8,
-1,
implicit,
implicit,
style,
)
} else if line.starts_with(b"=ALI") {
yaml_alias_event_initialize(event, get_anchor(b'*', line, anchor.as_mut_ptr()))
} else {
let line = line as *mut [u8] as *mut i8;
break Err(format!("Unknown event: '{}'", CStr::from_ptr(line)).into());
};
if result.fail {
break Err("Memory error: Not enough memory for creating an event".into());
}
if yaml_emitter_emit(emitter, event).fail {
break Err(match (*emitter).error {
YAML_MEMORY_ERROR => "Memory error: Not enough memory for emitting".into(),
YAML_WRITER_ERROR => {
format!("Writer error: {}", CStr::from_ptr((*emitter).problem)).into()
}
YAML_EMITTER_ERROR => {
format!("Emitter error: {}", CStr::from_ptr((*emitter).problem)).into()
}
// Couldn't happen.
_ => "Internal error".into(),
});
}
};
yaml_emitter_delete(emitter);
result
}
struct ReadBuf {
buf: [u8; 1024],
offset: usize,
filled: usize,
}
impl ReadBuf {
fn new() -> Self {
ReadBuf {
buf: [0; 1024],
offset: 0,
filled: 0,
}
}
fn get_line(&mut self, input: &mut dyn Read) -> Option<&mut [u8]> {
loop {
for i in self.offset..self.offset + self.filled {
if self.buf[i] == b'\n' {
self.buf[i] = b'\0';
let line = &mut self.buf[self.offset..=i];
self.offset = i + 1;
self.filled -= line.len();
return Some(line);
}
}
let mut remainder = &mut self.buf[self.offset + self.filled..];
if remainder.is_empty() {
if self.offset == 0 {
let _ = writeln!(
io::stderr(),
"Line too long: '{}'",
String::from_utf8_lossy(&self.buf),
);
process::abort();
}
self.buf.copy_within(self.offset.., 0);
self.offset = 0;
remainder = &mut self.buf;
}
let n = input.read(remainder).ok()?;
self.filled += n;
if n == 0 {
return None;
}
}
}
}
unsafe fn get_anchor(sigil: u8, line: &[u8], anchor: *mut u8) -> *mut u8 {
let start = match line.iter().position(|ch| *ch == sigil) {
Some(offset) => offset + 1,
None => return ptr::null_mut::<u8>(),
};
let end = match line[start..].iter().position(|ch| *ch == b' ') {
Some(offset) => start + offset,
None => line.len(),
};
ptr::copy_nonoverlapping(line[start..end].as_ptr(), anchor, end - start);
*anchor.add(end - start) = b'\0';
anchor
}
unsafe fn get_tag(line: &[u8], tag: *mut u8) -> *mut u8 {
let start = match line.iter().position(|ch| *ch == b'<') {
Some(offset) => offset + 1,
None => return ptr::null_mut::<u8>(),
};
let end = match line[start..].iter().position(|ch| *ch == b'>') {
Some(offset) => start + offset,
None => return ptr::null_mut::<u8>(),
};
ptr::copy_nonoverlapping(line[start..end].as_ptr(), tag, end - start);
*tag.add(end - start) = b'\0';
tag
}
unsafe fn get_value(line: &[u8], value: *mut i8, style: *mut yaml_scalar_style_t) {
let line_len = line.len();
let line = line as *const [u8] as *mut i8;
let mut start = ptr::null_mut::<i8>();
let end = line.add(line_len);
let mut c = line.offset(4);
while c < end {
if *c as u8 == b' ' {
start = c.offset(1);
*style = match *start as u8 {
b':' => YAML_PLAIN_SCALAR_STYLE,
b'\'' => YAML_SINGLE_QUOTED_SCALAR_STYLE,
b'"' => YAML_DOUBLE_QUOTED_SCALAR_STYLE,
b'|' => YAML_LITERAL_SCALAR_STYLE,
b'>' => YAML_FOLDED_SCALAR_STYLE,
_ => {
start = ptr::null_mut::<i8>();
c = c.offset(1);
continue;
}
};
start = start.offset(1);
break;
}
c = c.offset(1);
}
if start.is_null() {
process::abort();
}
let mut i = 0;
c = start;
while c < end {
*value.offset(i) = if *c as u8 == b'\\' {
c = c.offset(1);
match *c as u8 {
b'\\' => b'\\' as i8,
b'0' => b'\0' as i8,
b'b' => b'\x08' as i8,
b'n' => b'\n' as i8,
b'r' => b'\r' as i8,
b't' => b'\t' as i8,
_ => process::abort(),
}
} else {
*c
};
i += 1;
c = c.offset(1);
}
*value.offset(i) = b'\0' as i8;
}
fn main() -> ExitCode {
let args = env::args_os().skip(1);
if args.len() == 0 {
let _ = writeln!(
io::stderr(),
"Usage: run-emitter-test-suite <test.event>...",
);
return ExitCode::FAILURE;
}
for arg in args {
let mut stdin = File::open(arg).unwrap();
let mut stdout = io::stdout();
let result = unsafe { unsafe_main(&mut stdin, &mut stdout) };
if let Err(err) = result {
let _ = writeln!(io::stderr(), "{}", err);
return ExitCode::FAILURE;
}
}
ExitCode::SUCCESS
}