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