| #![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 |
| } |