term/terminfo/parser/
compiled.rs1use std::collections::HashMap;
14use std::io;
15use std::io::prelude::*;
16
17use crate::terminfo::Error::*;
18use crate::terminfo::TermInfo;
19use crate::Result;
20
21pub use crate::terminfo::parser::names::*;
22
23fn read_le_u16(r: &mut dyn io::Read) -> io::Result<u32> {
27 let mut buf = [0; 2];
28 r.read_exact(&mut buf)
29 .map(|()| u32::from(u16::from_le_bytes(buf)))
30}
31
32fn read_le_u32(r: &mut dyn io::Read) -> io::Result<u32> {
33 let mut buf = [0; 4];
34 r.read_exact(&mut buf).map(|()| u32::from_le_bytes(buf))
35}
36
37fn read_byte(r: &mut dyn io::Read) -> io::Result<u8> {
38 #[allow(clippy::unbuffered_bytes)]
41 match r.bytes().next() {
42 Some(s) => s,
43 None => Err(io::Error::new(io::ErrorKind::Other, "end of file")),
44 }
45}
46
47pub fn parse(file: &mut dyn io::Read, longnames: bool) -> Result<TermInfo> {
50 let (bnames, snames, nnames) = if longnames {
51 (boolfnames, stringfnames, numfnames)
52 } else {
53 (boolnames, stringnames, numnames)
54 };
55
56 let mut buf = [0; 2];
58 file.read_exact(&mut buf)?;
59 let magic = u16::from_le_bytes(buf);
60
61 let read_number = match magic {
62 0x011A => read_le_u16,
63 0x021e => read_le_u32,
64 _ => return Err(BadMagic(magic).into()),
65 };
66
67 macro_rules! read_nonneg {
72 () => {{
73 match read_le_u16(file)? as i16 {
74 n if n >= 0 => n as usize,
75 -1 => 0,
76 _ => return Err(InvalidLength.into()),
77 }
78 }};
79 }
80
81 let names_bytes = read_nonneg!();
82 let bools_bytes = read_nonneg!();
83 let numbers_count = read_nonneg!();
84 let string_offsets_count = read_nonneg!();
85 let string_table_bytes = read_nonneg!();
86
87 if names_bytes == 0 {
88 return Err(ShortNames.into());
89 }
90
91 if bools_bytes > boolnames.len() {
92 return Err(TooManyBools.into());
93 }
94
95 if numbers_count > numnames.len() {
96 return Err(TooManyNumbers.into());
97 }
98
99 if string_offsets_count > stringnames.len() {
100 return Err(TooManyStrings.into());
101 }
102
103 let mut bytes = Vec::new();
105 file.take((names_bytes - 1) as u64)
106 .read_to_end(&mut bytes)?;
107 let names_str = match String::from_utf8(bytes) {
108 Ok(s) => s,
109 Err(e) => return Err(NotUtf8(e.utf8_error()).into()),
110 };
111
112 let term_names: Vec<String> = names_str.split('|').map(|s| s.to_owned()).collect();
113 if read_byte(file)? != b'\0' {
115 return Err(NamesMissingNull.into());
116 }
117
118 let bools_map = (0..bools_bytes)
119 .filter_map(|i| match read_byte(file) {
120 Err(e) => Some(Err(e)),
121 Ok(1) => Some(Ok((bnames[i], true))),
122 Ok(_) => None,
123 })
124 .collect::<io::Result<HashMap<_, _>>>()?;
125
126 if (bools_bytes + names_bytes) % 2 == 1 {
127 read_byte(file)?; }
129
130 let numbers_map = (0..numbers_count)
131 .filter_map(|i| match read_number(file) {
132 Ok(0xFFFF) => None,
133 Ok(n) => Some(Ok((nnames[i], n))),
134 Err(e) => Some(Err(e)),
135 })
136 .collect::<io::Result<HashMap<_, _>>>()?;
137
138 let string_map: HashMap<&str, Vec<u8>> = if string_offsets_count > 0 {
139 let string_offsets = (0..string_offsets_count)
140 .map(|_| {
141 let mut buf = [0; 2];
142 file.read_exact(&mut buf).map(|()| u16::from_le_bytes(buf))
143 })
144 .collect::<io::Result<Vec<_>>>()?;
145
146 let mut string_table = Vec::new();
147 file.take(string_table_bytes as u64)
148 .read_to_end(&mut string_table)?;
149
150 string_offsets
151 .into_iter()
152 .enumerate()
153 .filter(|&(_, offset)| {
154 offset != 0xFFFF
156 })
157 .map(|(i, offset)| {
158 let offset = offset as usize;
159
160 let name = if snames[i] == "_" {
161 stringfnames[i]
162 } else {
163 snames[i]
164 };
165
166 if offset == 0xFFFE {
167 return Ok((name, Vec::new()));
171 }
172
173 let nulpos = string_table[offset..string_table_bytes]
175 .iter()
176 .position(|&b| b == 0);
177 match nulpos {
178 Some(len) => Ok((name, string_table[offset..offset + len].to_vec())),
179 None => Err(crate::Error::TerminfoParsing(StringsMissingNull)),
180 }
181 })
182 .collect::<Result<HashMap<_, _>>>()?
183 } else {
184 HashMap::new()
185 };
186
187 Ok(TermInfo {
189 names: term_names,
190 bools: bools_map,
191 numbers: numbers_map,
192 strings: string_map,
193 })
194}
195
196#[cfg(test)]
197mod test {
198
199 use super::{boolfnames, boolnames, numfnames, numnames, stringfnames, stringnames};
200
201 #[test]
202 fn test_veclens() {
203 assert_eq!(boolfnames.len(), boolnames.len());
204 assert_eq!(numfnames.len(), numnames.len());
205 assert_eq!(stringfnames.len(), stringnames.len());
206 }
207}