1 #![warn(clippy::pedantic)]
2 #![allow(
3     clippy::cast_lossless,
4     clippy::cast_possible_truncation,
5     clippy::cast_possible_wrap,
6     clippy::cast_sign_loss,
7     clippy::items_after_statements,
8     clippy::let_underscore_untyped,
9     clippy::missing_errors_doc,
10     clippy::missing_safety_doc,
11     clippy::ptr_as_ptr,
12     clippy::single_match_else,
13     clippy::too_many_lines,
14     clippy::unreadable_literal
15 )]
16 
17 mod cstr;
18 
19 use self::cstr::CStr;
20 use std::env;
21 use std::error::Error;
22 use std::ffi::c_void;
23 use std::fs::File;
24 use std::io::{self, Read, Write};
25 use std::mem::MaybeUninit;
26 use std::process::{self, ExitCode};
27 use std::ptr::{self, addr_of_mut};
28 use std::slice;
29 use unsafe_libyaml::{
30     yaml_alias_event_initialize, yaml_document_end_event_initialize,
31     yaml_document_start_event_initialize, yaml_emitter_delete, yaml_emitter_emit,
32     yaml_emitter_initialize, yaml_emitter_set_canonical, yaml_emitter_set_output,
33     yaml_emitter_set_unicode, yaml_emitter_t, yaml_event_t, yaml_mapping_end_event_initialize,
34     yaml_mapping_start_event_initialize, yaml_scalar_event_initialize, yaml_scalar_style_t,
35     yaml_sequence_end_event_initialize, yaml_sequence_start_event_initialize,
36     yaml_stream_end_event_initialize, yaml_stream_start_event_initialize, yaml_tag_directive_t,
37     yaml_version_directive_t, YAML_ANY_SCALAR_STYLE, YAML_BLOCK_MAPPING_STYLE,
38     YAML_BLOCK_SEQUENCE_STYLE, YAML_DOUBLE_QUOTED_SCALAR_STYLE, YAML_EMITTER_ERROR,
39     YAML_FOLDED_SCALAR_STYLE, YAML_LITERAL_SCALAR_STYLE, YAML_MEMORY_ERROR,
40     YAML_PLAIN_SCALAR_STYLE, YAML_SINGLE_QUOTED_SCALAR_STYLE, YAML_UTF8_ENCODING,
41     YAML_WRITER_ERROR,
42 };
43 
unsafe_main( stdin: &mut dyn Read, mut stdout: &mut dyn Write, ) -> Result<(), Box<dyn Error>>44 pub(crate) unsafe fn unsafe_main(
45     stdin: &mut dyn Read,
46     mut stdout: &mut dyn Write,
47 ) -> Result<(), Box<dyn Error>> {
48     let mut emitter = MaybeUninit::<yaml_emitter_t>::uninit();
49     let emitter = emitter.as_mut_ptr();
50     if yaml_emitter_initialize(emitter).fail {
51         return Err("Could not initalize the emitter object".into());
52     }
53 
54     unsafe fn write_to_stdio(data: *mut c_void, buffer: *mut u8, size: u64) -> i32 {
55         let stdout: *mut &mut dyn Write = data.cast();
56         let bytes = slice::from_raw_parts(buffer.cast(), size as usize);
57         match (*stdout).write(bytes) {
58             Ok(n) => n as i32,
59             Err(_) => 0,
60         }
61     }
62 
63     yaml_emitter_set_output(emitter, write_to_stdio, addr_of_mut!(stdout).cast());
64     yaml_emitter_set_canonical(emitter, false);
65     yaml_emitter_set_unicode(emitter, false);
66 
67     let mut buf = ReadBuf::new();
68     let mut event = MaybeUninit::<yaml_event_t>::uninit();
69     let event = event.as_mut_ptr();
70     let result = loop {
71         let line = match buf.get_line(stdin) {
72             Some(line) => line,
73             None => break Ok(()),
74         };
75 
76         let mut anchor = [0u8; 256];
77         let mut tag = [0u8; 256];
78         let result = if line.starts_with(b"+STR") {
79             yaml_stream_start_event_initialize(event, YAML_UTF8_ENCODING)
80         } else if line.starts_with(b"-STR") {
81             yaml_stream_end_event_initialize(event)
82         } else if line.starts_with(b"+DOC") {
83             let implicit = !line[4..].starts_with(b" ---");
84             yaml_document_start_event_initialize(
85                 event,
86                 ptr::null_mut::<yaml_version_directive_t>(),
87                 ptr::null_mut::<yaml_tag_directive_t>(),
88                 ptr::null_mut::<yaml_tag_directive_t>(),
89                 implicit,
90             )
91         } else if line.starts_with(b"-DOC") {
92             let implicit = !line[4..].starts_with(b" ...");
93             yaml_document_end_event_initialize(event, implicit)
94         } else if line.starts_with(b"+MAP") {
95             yaml_mapping_start_event_initialize(
96                 event,
97                 get_anchor(b'&', line, anchor.as_mut_ptr()),
98                 get_tag(line, tag.as_mut_ptr()),
99                 false,
100                 YAML_BLOCK_MAPPING_STYLE,
101             )
102         } else if line.starts_with(b"-MAP") {
103             yaml_mapping_end_event_initialize(event)
104         } else if line.starts_with(b"+SEQ") {
105             yaml_sequence_start_event_initialize(
106                 event,
107                 get_anchor(b'&', line, anchor.as_mut_ptr()),
108                 get_tag(line, tag.as_mut_ptr()),
109                 false,
110                 YAML_BLOCK_SEQUENCE_STYLE,
111             )
112         } else if line.starts_with(b"-SEQ") {
113             yaml_sequence_end_event_initialize(event)
114         } else if line.starts_with(b"=VAL") {
115             let mut value = [0i8; 1024];
116             let mut style = YAML_ANY_SCALAR_STYLE;
117             get_value(line, value.as_mut_ptr(), &mut style);
118             let implicit = get_tag(line, tag.as_mut_ptr()).is_null();
119             yaml_scalar_event_initialize(
120                 event,
121                 get_anchor(b'&', line, anchor.as_mut_ptr()),
122                 get_tag(line, tag.as_mut_ptr()),
123                 value.as_mut_ptr() as *mut u8,
124                 -1,
125                 implicit,
126                 implicit,
127                 style,
128             )
129         } else if line.starts_with(b"=ALI") {
130             yaml_alias_event_initialize(event, get_anchor(b'*', line, anchor.as_mut_ptr()))
131         } else {
132             let line = line as *mut [u8] as *mut i8;
133             break Err(format!("Unknown event: '{}'", CStr::from_ptr(line)).into());
134         };
135 
136         if result.fail {
137             break Err("Memory error: Not enough memory for creating an event".into());
138         }
139         if yaml_emitter_emit(emitter, event).fail {
140             break Err(match (*emitter).error {
141                 YAML_MEMORY_ERROR => "Memory error: Not enough memory for emitting".into(),
142                 YAML_WRITER_ERROR => {
143                     format!("Writer error: {}", CStr::from_ptr((*emitter).problem)).into()
144                 }
145                 YAML_EMITTER_ERROR => {
146                     format!("Emitter error: {}", CStr::from_ptr((*emitter).problem)).into()
147                 }
148                 // Couldn't happen.
149                 _ => "Internal error".into(),
150             });
151         }
152     };
153 
154     yaml_emitter_delete(emitter);
155     result
156 }
157 
158 struct ReadBuf {
159     buf: [u8; 1024],
160     offset: usize,
161     filled: usize,
162 }
163 
164 impl ReadBuf {
new() -> Self165     fn new() -> Self {
166         ReadBuf {
167             buf: [0; 1024],
168             offset: 0,
169             filled: 0,
170         }
171     }
172 
get_line(&mut self, input: &mut dyn Read) -> Option<&mut [u8]>173     fn get_line(&mut self, input: &mut dyn Read) -> Option<&mut [u8]> {
174         loop {
175             for i in self.offset..self.offset + self.filled {
176                 if self.buf[i] == b'\n' {
177                     self.buf[i] = b'\0';
178                     let line = &mut self.buf[self.offset..=i];
179                     self.offset = i + 1;
180                     self.filled -= line.len();
181                     return Some(line);
182                 }
183             }
184             let mut remainder = &mut self.buf[self.offset + self.filled..];
185             if remainder.is_empty() {
186                 if self.offset == 0 {
187                     let _ = writeln!(
188                         io::stderr(),
189                         "Line too long: '{}'",
190                         String::from_utf8_lossy(&self.buf),
191                     );
192                     process::abort();
193                 }
194                 self.buf.copy_within(self.offset.., 0);
195                 self.offset = 0;
196                 remainder = &mut self.buf;
197             }
198             let n = input.read(remainder).ok()?;
199             self.filled += n;
200             if n == 0 {
201                 return None;
202             }
203         }
204     }
205 }
206 
get_anchor(sigil: u8, line: &[u8], anchor: *mut u8) -> *mut u8207 unsafe fn get_anchor(sigil: u8, line: &[u8], anchor: *mut u8) -> *mut u8 {
208     let start = match line.iter().position(|ch| *ch == sigil) {
209         Some(offset) => offset + 1,
210         None => return ptr::null_mut::<u8>(),
211     };
212     let end = match line[start..].iter().position(|ch| *ch == b' ') {
213         Some(offset) => start + offset,
214         None => line.len(),
215     };
216     ptr::copy_nonoverlapping(line[start..end].as_ptr(), anchor, end - start);
217     *anchor.add(end - start) = b'\0';
218     anchor
219 }
220 
get_tag(line: &[u8], tag: *mut u8) -> *mut u8221 unsafe fn get_tag(line: &[u8], tag: *mut u8) -> *mut u8 {
222     let start = match line.iter().position(|ch| *ch == b'<') {
223         Some(offset) => offset + 1,
224         None => return ptr::null_mut::<u8>(),
225     };
226     let end = match line[start..].iter().position(|ch| *ch == b'>') {
227         Some(offset) => start + offset,
228         None => return ptr::null_mut::<u8>(),
229     };
230     ptr::copy_nonoverlapping(line[start..end].as_ptr(), tag, end - start);
231     *tag.add(end - start) = b'\0';
232     tag
233 }
234 
get_value(line: &[u8], value: *mut i8, style: *mut yaml_scalar_style_t)235 unsafe fn get_value(line: &[u8], value: *mut i8, style: *mut yaml_scalar_style_t) {
236     let line_len = line.len();
237     let line = line as *const [u8] as *mut i8;
238     let mut start = ptr::null_mut::<i8>();
239     let end = line.add(line_len);
240     let mut c = line.offset(4);
241     while c < end {
242         if *c as u8 == b' ' {
243             start = c.offset(1);
244             *style = match *start as u8 {
245                 b':' => YAML_PLAIN_SCALAR_STYLE,
246                 b'\'' => YAML_SINGLE_QUOTED_SCALAR_STYLE,
247                 b'"' => YAML_DOUBLE_QUOTED_SCALAR_STYLE,
248                 b'|' => YAML_LITERAL_SCALAR_STYLE,
249                 b'>' => YAML_FOLDED_SCALAR_STYLE,
250                 _ => {
251                     start = ptr::null_mut::<i8>();
252                     c = c.offset(1);
253                     continue;
254                 }
255             };
256             start = start.offset(1);
257             break;
258         }
259         c = c.offset(1);
260     }
261     if start.is_null() {
262         process::abort();
263     }
264 
265     let mut i = 0;
266     c = start;
267     while c < end {
268         *value.offset(i) = if *c as u8 == b'\\' {
269             c = c.offset(1);
270             match *c as u8 {
271                 b'\\' => b'\\' as i8,
272                 b'0' => b'\0' as i8,
273                 b'b' => b'\x08' as i8,
274                 b'n' => b'\n' as i8,
275                 b'r' => b'\r' as i8,
276                 b't' => b'\t' as i8,
277                 _ => process::abort(),
278             }
279         } else {
280             *c
281         };
282         i += 1;
283         c = c.offset(1);
284     }
285     *value.offset(i) = b'\0' as i8;
286 }
287 
main() -> ExitCode288 fn main() -> ExitCode {
289     let args = env::args_os().skip(1);
290     if args.len() == 0 {
291         let _ = writeln!(
292             io::stderr(),
293             "Usage: run-emitter-test-suite <test.event>...",
294         );
295         return ExitCode::FAILURE;
296     }
297     for arg in args {
298         let mut stdin = File::open(arg).unwrap();
299         let mut stdout = io::stdout();
300         let result = unsafe { unsafe_main(&mut stdin, &mut stdout) };
301         if let Err(err) = result {
302             let _ = writeln!(io::stderr(), "{}", err);
303             return ExitCode::FAILURE;
304         }
305     }
306     ExitCode::SUCCESS
307 }
308