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