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