Skip to main content

run_emitter_test_suite/
run-emitter-test-suite.rs

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// c2rust transliteration: unsafe operations are called directly inside `unsafe fn` bodies.
18// Same rationale as `unsafe_libyaml/src/lib.rs`.
19#![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                // Couldn't happen.
147                _ => "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}