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