#![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> { let mut emitter = MaybeUninit::::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::::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::(), ptr::null_mut::(), ptr::null_mut::(), 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::(), }; 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::(), }; let end = match line[start..].iter().position(|ch| *ch == b'>') { Some(offset) => start + offset, None => return ptr::null_mut::(), }; 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::(); 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::(); 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 ...", ); 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 }