#![allow(missing_docs)]
#![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::uninlined_format_args,
clippy::unreadable_literal
)]
mod cstr;
use self::cstr::CStr;
use core::fmt;
pub(crate) use core::primitive::u8 as yaml_char_t;
use libyml::api::ScalarEventData;
use libyml::document::{
yaml_document_end_event_initialize,
yaml_document_start_event_initialize,
};
use libyml::{
yaml_alias_event_initialize, yaml_emitter_delete,
yaml_emitter_emit, yaml_emitter_initialize,
yaml_emitter_set_canonical, yaml_emitter_set_output,
yaml_emitter_set_unicode, yaml_mapping_end_event_initialize,
yaml_mapping_start_event_initialize, yaml_scalar_event_initialize,
yaml_sequence_end_event_initialize,
yaml_sequence_start_event_initialize,
yaml_stream_end_event_initialize,
yaml_stream_start_event_initialize, YamlAnyScalarStyle,
YamlBlockMappingStyle, YamlBlockSequenceStyle,
YamlDoubleQuotedScalarStyle, YamlEmitterError, YamlEmitterT,
YamlEventT, YamlFoldedScalarStyle, YamlLiteralScalarStyle,
YamlMemoryError, YamlPlainScalarStyle, YamlScalarStyleT,
YamlSingleQuotedScalarStyle, YamlTagDirectiveT, YamlUtf8Encoding,
YamlVersionDirectiveT, YamlWriterError,
};
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};
#[derive(Debug)]
pub(crate) enum EmitterError {
InitializationError(String),
MemoryError(String),
UnknownEvent(String),
EmittingError(String),
InternalError,
}
impl fmt::Display for EmitterError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EmitterError::InitializationError(msg)
| EmitterError::MemoryError(msg)
| EmitterError::UnknownEvent(msg)
| EmitterError::EmittingError(msg) => {
write!(f, "{}", msg)
}
EmitterError::InternalError => write!(f, "Internal error"),
}
}
}
impl Error for EmitterError {}
pub(crate) unsafe fn unsafe_main(
stdin: &mut dyn Read,
mut stdout: &mut dyn Write,
) -> Result<(), EmitterError> {
let mut emitter = MaybeUninit::<YamlEmitterT>::uninit();
let emitter = emitter.as_mut_ptr();
if yaml_emitter_initialize(emitter).fail {
return Err(EmitterError::InitializationError(
"Could not initialize 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 as _;
let bytes = std::slice::from_raw_parts(buffer, 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::<YamlEventT>::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, YamlUtf8Encoding)
} 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::<YamlVersionDirectiveT>(),
ptr::null_mut::<YamlTagDirectiveT>(),
ptr::null_mut::<YamlTagDirectiveT>(),
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,
YamlBlockMappingStyle,
)
} 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,
YamlBlockSequenceStyle,
)
} 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 = YamlAnyScalarStyle;
get_value(line, value.as_mut_ptr(), &mut style);
let implicit = get_tag(line, tag.as_mut_ptr()).is_null();
let scalar_event_data = ScalarEventData {
anchor: get_anchor(b'&', line, anchor.as_mut_ptr()),
tag: get_tag(line, tag.as_mut_ptr()),
value: value.as_mut_ptr() as *const yaml_char_t,
length: -1,
plain_implicit: implicit,
quoted_implicit: implicit,
style,
_marker: core::marker::PhantomData,
};
yaml_scalar_event_initialize(event, scalar_event_data)
} 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_ptr() as *mut i8;
break Err(EmitterError::UnknownEvent(format!(
"Unknown event: '{}'",
CStr::from_ptr(line)
)));
};
if result.fail {
break Err(EmitterError::MemoryError(
"Memory error: Not enough memory for creating an event"
.into(),
));
}
if yaml_emitter_emit(emitter, event).fail {
break Err(match (*emitter).error {
YamlMemoryError => EmitterError::MemoryError(
"Memory error: Not enough memory for emitting"
.into(),
),
YamlWriterError => {
EmitterError::EmittingError(format!(
"Writer error: {}",
CStr::from_ptr((*emitter).problem)
))
}
YamlEmitterError => {
EmitterError::EmittingError(format!(
"Emitter error: {}",
CStr::from_ptr((*emitter).problem)
))
}
_ => EmitterError::InternalError,
});
}
};
yaml_emitter_delete(emitter);
result.map_err(Into::into)
}
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(),
};
let end = match line[start..].iter().position(|ch| *ch == b' ') {
Some(offset) => start + offset,
None => line.len(),
};
anchor.copy_from_nonoverlapping(
line[start..end].as_ptr(),
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(),
};
let end = match line[start..].iter().position(|ch| *ch == b'>') {
Some(offset) => start + offset,
None => return ptr::null_mut(),
};
tag.copy_from_nonoverlapping(
line[start..end].as_ptr(),
end - start,
);
*tag.add(end - start) = b'\0';
tag
}
unsafe fn get_value(
line: &[u8],
value: *mut i8,
style: *mut YamlScalarStyleT,
) {
let line_len = line.len();
let line = line.as_ptr() 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':' => YamlPlainScalarStyle,
b'\'' => YamlSingleQuotedScalarStyle,
b'"' => YamlDoubleQuotedScalarStyle,
b'|' => YamlLiteralScalarStyle,
b'>' => YamlFoldedScalarStyle,
_ => {
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
}