]> git.proxmox.com Git - rustc.git/blob - src/libterm/terminfo/parser/compiled.rs
Imported Upstream version 1.9.0+dfsg1
[rustc.git] / src / libterm / terminfo / parser / compiled.rs
1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 #![allow(non_upper_case_globals, missing_docs)]
12
13 //! ncurses-compatible compiled terminfo format parsing (term(5))
14
15 use std::collections::HashMap;
16 use std::io::prelude::*;
17 use std::io;
18 use super::super::TermInfo;
19
20 // These are the orders ncurses uses in its compiled format (as of 5.9). Not sure if portable.
21
22 #[rustfmt_skip]
23 pub static boolfnames: &'static[&'static str] = &["auto_left_margin", "auto_right_margin",
24 "no_esc_ctlc", "ceol_standout_glitch", "eat_newline_glitch", "erase_overstrike", "generic_type",
25 "hard_copy", "has_meta_key", "has_status_line", "insert_null_glitch", "memory_above",
26 "memory_below", "move_insert_mode", "move_standout_mode", "over_strike", "status_line_esc_ok",
27 "dest_tabs_magic_smso", "tilde_glitch", "transparent_underline", "xon_xoff", "needs_xon_xoff",
28 "prtr_silent", "hard_cursor", "non_rev_rmcup", "no_pad_char", "non_dest_scroll_region",
29 "can_change", "back_color_erase", "hue_lightness_saturation", "col_addr_glitch",
30 "cr_cancels_micro_mode", "has_print_wheel", "row_addr_glitch", "semi_auto_right_margin",
31 "cpi_changes_res", "lpi_changes_res", "backspaces_with_bs", "crt_no_scrolling",
32 "no_correctly_working_cr", "gnu_has_meta_key", "linefeed_is_newline", "has_hardware_tabs",
33 "return_does_clr_eol"];
34
35 #[rustfmt_skip]
36 pub static boolnames: &'static[&'static str] = &["bw", "am", "xsb", "xhp", "xenl", "eo",
37 "gn", "hc", "km", "hs", "in", "db", "da", "mir", "msgr", "os", "eslok", "xt", "hz", "ul", "xon",
38 "nxon", "mc5i", "chts", "nrrmc", "npc", "ndscr", "ccc", "bce", "hls", "xhpa", "crxm", "daisy",
39 "xvpa", "sam", "cpix", "lpix", "OTbs", "OTns", "OTnc", "OTMT", "OTNL", "OTpt", "OTxr"];
40
41 #[rustfmt_skip]
42 pub static numfnames: &'static[&'static str] = &[ "columns", "init_tabs", "lines",
43 "lines_of_memory", "magic_cookie_glitch", "padding_baud_rate", "virtual_terminal",
44 "width_status_line", "num_labels", "label_height", "label_width", "max_attributes",
45 "maximum_windows", "max_colors", "max_pairs", "no_color_video", "buffer_capacity",
46 "dot_vert_spacing", "dot_horz_spacing", "max_micro_address", "max_micro_jump", "micro_col_size",
47 "micro_line_size", "number_of_pins", "output_res_char", "output_res_line",
48 "output_res_horz_inch", "output_res_vert_inch", "print_rate", "wide_char_size", "buttons",
49 "bit_image_entwining", "bit_image_type", "magic_cookie_glitch_ul", "carriage_return_delay",
50 "new_line_delay", "backspace_delay", "horizontal_tab_delay", "number_of_function_keys"];
51
52 #[rustfmt_skip]
53 pub static numnames: &'static[&'static str] = &[ "cols", "it", "lines", "lm", "xmc", "pb",
54 "vt", "wsl", "nlab", "lh", "lw", "ma", "wnum", "colors", "pairs", "ncv", "bufsz", "spinv",
55 "spinh", "maddr", "mjump", "mcs", "mls", "npins", "orc", "orl", "orhi", "orvi", "cps", "widcs",
56 "btns", "bitwin", "bitype", "UTug", "OTdC", "OTdN", "OTdB", "OTdT", "OTkn"];
57
58 #[rustfmt_skip]
59 pub static stringfnames: &'static[&'static str] = &[ "back_tab", "bell", "carriage_return",
60 "change_scroll_region", "clear_all_tabs", "clear_screen", "clr_eol", "clr_eos",
61 "column_address", "command_character", "cursor_address", "cursor_down", "cursor_home",
62 "cursor_invisible", "cursor_left", "cursor_mem_address", "cursor_normal", "cursor_right",
63 "cursor_to_ll", "cursor_up", "cursor_visible", "delete_character", "delete_line",
64 "dis_status_line", "down_half_line", "enter_alt_charset_mode", "enter_blink_mode",
65 "enter_bold_mode", "enter_ca_mode", "enter_delete_mode", "enter_dim_mode", "enter_insert_mode",
66 "enter_secure_mode", "enter_protected_mode", "enter_reverse_mode", "enter_standout_mode",
67 "enter_underline_mode", "erase_chars", "exit_alt_charset_mode", "exit_attribute_mode",
68 "exit_ca_mode", "exit_delete_mode", "exit_insert_mode", "exit_standout_mode",
69 "exit_underline_mode", "flash_screen", "form_feed", "from_status_line", "init_1string",
70 "init_2string", "init_3string", "init_file", "insert_character", "insert_line",
71 "insert_padding", "key_backspace", "key_catab", "key_clear", "key_ctab", "key_dc", "key_dl",
72 "key_down", "key_eic", "key_eol", "key_eos", "key_f0", "key_f1", "key_f10", "key_f2", "key_f3",
73 "key_f4", "key_f5", "key_f6", "key_f7", "key_f8", "key_f9", "key_home", "key_ic", "key_il",
74 "key_left", "key_ll", "key_npage", "key_ppage", "key_right", "key_sf", "key_sr", "key_stab",
75 "key_up", "keypad_local", "keypad_xmit", "lab_f0", "lab_f1", "lab_f10", "lab_f2", "lab_f3",
76 "lab_f4", "lab_f5", "lab_f6", "lab_f7", "lab_f8", "lab_f9", "meta_off", "meta_on", "newline",
77 "pad_char", "parm_dch", "parm_delete_line", "parm_down_cursor", "parm_ich", "parm_index",
78 "parm_insert_line", "parm_left_cursor", "parm_right_cursor", "parm_rindex", "parm_up_cursor",
79 "pkey_key", "pkey_local", "pkey_xmit", "print_screen", "prtr_off", "prtr_on", "repeat_char",
80 "reset_1string", "reset_2string", "reset_3string", "reset_file", "restore_cursor",
81 "row_address", "save_cursor", "scroll_forward", "scroll_reverse", "set_attributes", "set_tab",
82 "set_window", "tab", "to_status_line", "underline_char", "up_half_line", "init_prog", "key_a1",
83 "key_a3", "key_b2", "key_c1", "key_c3", "prtr_non", "char_padding", "acs_chars", "plab_norm",
84 "key_btab", "enter_xon_mode", "exit_xon_mode", "enter_am_mode", "exit_am_mode", "xon_character",
85 "xoff_character", "ena_acs", "label_on", "label_off", "key_beg", "key_cancel", "key_close",
86 "key_command", "key_copy", "key_create", "key_end", "key_enter", "key_exit", "key_find",
87 "key_help", "key_mark", "key_message", "key_move", "key_next", "key_open", "key_options",
88 "key_previous", "key_print", "key_redo", "key_reference", "key_refresh", "key_replace",
89 "key_restart", "key_resume", "key_save", "key_suspend", "key_undo", "key_sbeg", "key_scancel",
90 "key_scommand", "key_scopy", "key_screate", "key_sdc", "key_sdl", "key_select", "key_send",
91 "key_seol", "key_sexit", "key_sfind", "key_shelp", "key_shome", "key_sic", "key_sleft",
92 "key_smessage", "key_smove", "key_snext", "key_soptions", "key_sprevious", "key_sprint",
93 "key_sredo", "key_sreplace", "key_sright", "key_srsume", "key_ssave", "key_ssuspend",
94 "key_sundo", "req_for_input", "key_f11", "key_f12", "key_f13", "key_f14", "key_f15", "key_f16",
95 "key_f17", "key_f18", "key_f19", "key_f20", "key_f21", "key_f22", "key_f23", "key_f24",
96 "key_f25", "key_f26", "key_f27", "key_f28", "key_f29", "key_f30", "key_f31", "key_f32",
97 "key_f33", "key_f34", "key_f35", "key_f36", "key_f37", "key_f38", "key_f39", "key_f40",
98 "key_f41", "key_f42", "key_f43", "key_f44", "key_f45", "key_f46", "key_f47", "key_f48",
99 "key_f49", "key_f50", "key_f51", "key_f52", "key_f53", "key_f54", "key_f55", "key_f56",
100 "key_f57", "key_f58", "key_f59", "key_f60", "key_f61", "key_f62", "key_f63", "clr_bol",
101 "clear_margins", "set_left_margin", "set_right_margin", "label_format", "set_clock",
102 "display_clock", "remove_clock", "create_window", "goto_window", "hangup", "dial_phone",
103 "quick_dial", "tone", "pulse", "flash_hook", "fixed_pause", "wait_tone", "user0", "user1",
104 "user2", "user3", "user4", "user5", "user6", "user7", "user8", "user9", "orig_pair",
105 "orig_colors", "initialize_color", "initialize_pair", "set_color_pair", "set_foreground",
106 "set_background", "change_char_pitch", "change_line_pitch", "change_res_horz",
107 "change_res_vert", "define_char", "enter_doublewide_mode", "enter_draft_quality",
108 "enter_italics_mode", "enter_leftward_mode", "enter_micro_mode", "enter_near_letter_quality",
109 "enter_normal_quality", "enter_shadow_mode", "enter_subscript_mode", "enter_superscript_mode",
110 "enter_upward_mode", "exit_doublewide_mode", "exit_italics_mode", "exit_leftward_mode",
111 "exit_micro_mode", "exit_shadow_mode", "exit_subscript_mode", "exit_superscript_mode",
112 "exit_upward_mode", "micro_column_address", "micro_down", "micro_left", "micro_right",
113 "micro_row_address", "micro_up", "order_of_pins", "parm_down_micro", "parm_left_micro",
114 "parm_right_micro", "parm_up_micro", "select_char_set", "set_bottom_margin",
115 "set_bottom_margin_parm", "set_left_margin_parm", "set_right_margin_parm", "set_top_margin",
116 "set_top_margin_parm", "start_bit_image", "start_char_set_def", "stop_bit_image",
117 "stop_char_set_def", "subscript_characters", "superscript_characters", "these_cause_cr",
118 "zero_motion", "char_set_names", "key_mouse", "mouse_info", "req_mouse_pos", "get_mouse",
119 "set_a_foreground", "set_a_background", "pkey_plab", "device_type", "code_set_init",
120 "set0_des_seq", "set1_des_seq", "set2_des_seq", "set3_des_seq", "set_lr_margin",
121 "set_tb_margin", "bit_image_repeat", "bit_image_newline", "bit_image_carriage_return",
122 "color_names", "define_bit_image_region", "end_bit_image_region", "set_color_band",
123 "set_page_length", "display_pc_char", "enter_pc_charset_mode", "exit_pc_charset_mode",
124 "enter_scancode_mode", "exit_scancode_mode", "pc_term_options", "scancode_escape",
125 "alt_scancode_esc", "enter_horizontal_hl_mode", "enter_left_hl_mode", "enter_low_hl_mode",
126 "enter_right_hl_mode", "enter_top_hl_mode", "enter_vertical_hl_mode", "set_a_attributes",
127 "set_pglen_inch", "termcap_init2", "termcap_reset", "linefeed_if_not_lf", "backspace_if_not_bs",
128 "other_non_function_keys", "arrow_key_map", "acs_ulcorner", "acs_llcorner", "acs_urcorner",
129 "acs_lrcorner", "acs_ltee", "acs_rtee", "acs_btee", "acs_ttee", "acs_hline", "acs_vline",
130 "acs_plus", "memory_lock", "memory_unlock", "box_chars_1"];
131
132 #[rustfmt_skip]
133 pub static stringnames: &'static[&'static str] = &[ "cbt", "_", "cr", "csr", "tbc", "clear",
134 "_", "_", "hpa", "cmdch", "cup", "cud1", "home", "civis", "cub1", "mrcup", "cnorm", "cuf1",
135 "ll", "cuu1", "cvvis", "dch1", "dl1", "dsl", "hd", "smacs", "blink", "bold", "smcup", "smdc",
136 "dim", "smir", "invis", "prot", "rev", "smso", "smul", "ech", "rmacs", "sgr0", "rmcup", "rmdc",
137 "rmir", "rmso", "rmul", "flash", "ff", "fsl", "is1", "is2", "is3", "if", "ich1", "il1", "ip",
138 "kbs", "ktbc", "kclr", "kctab", "_", "_", "kcud1", "_", "_", "_", "_", "_", "_", "_", "_", "_",
139 "_", "_", "_", "_", "_", "khome", "_", "_", "kcub1", "_", "knp", "kpp", "kcuf1", "_", "_",
140 "khts", "_", "rmkx", "smkx", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "rmm", "_",
141 "_", "pad", "dch", "dl", "cud", "ich", "indn", "il", "cub", "cuf", "rin", "cuu", "pfkey",
142 "pfloc", "pfx", "mc0", "mc4", "_", "rep", "rs1", "rs2", "rs3", "rf", "rc", "vpa", "sc", "ind",
143 "ri", "sgr", "_", "wind", "_", "tsl", "uc", "hu", "iprog", "_", "_", "_", "_", "_", "mc5p",
144 "rmp", "acsc", "pln", "kcbt", "smxon", "rmxon", "smam", "rmam", "xonc", "xoffc", "_", "smln",
145 "rmln", "_", "kcan", "kclo", "kcmd", "kcpy", "kcrt", "_", "kent", "kext", "kfnd", "khlp",
146 "kmrk", "kmsg", "kmov", "knxt", "kopn", "kopt", "kprv", "kprt", "krdo", "kref", "krfr", "krpl",
147 "krst", "kres", "ksav", "kspd", "kund", "kBEG", "kCAN", "kCMD", "kCPY", "kCRT", "_", "_",
148 "kslt", "kEND", "kEOL", "kEXT", "kFND", "kHLP", "kHOM", "_", "kLFT", "kMSG", "kMOV", "kNXT",
149 "kOPT", "kPRV", "kPRT", "kRDO", "kRPL", "kRIT", "kRES", "kSAV", "kSPD", "kUND", "rfi", "_", "_",
150 "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_",
151 "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_",
152 "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_",
153 "dclk", "rmclk", "cwin", "wingo", "_", "dial", "qdial", "_", "_", "hook", "pause", "wait", "_",
154 "_", "_", "_", "_", "_", "_", "_", "_", "_", "op", "oc", "initc", "initp", "scp", "setf",
155 "setb", "cpi", "lpi", "chr", "cvr", "defc", "swidm", "sdrfq", "sitm", "slm", "smicm", "snlq",
156 "snrmq", "sshm", "ssubm", "ssupm", "sum", "rwidm", "ritm", "rlm", "rmicm", "rshm", "rsubm",
157 "rsupm", "rum", "mhpa", "mcud1", "mcub1", "mcuf1", "mvpa", "mcuu1", "porder", "mcud", "mcub",
158 "mcuf", "mcuu", "scs", "smgb", "smgbp", "smglp", "smgrp", "smgt", "smgtp", "sbim", "scsd",
159 "rbim", "rcsd", "subcs", "supcs", "docr", "zerom", "csnm", "kmous", "minfo", "reqmp", "getm",
160 "setaf", "setab", "pfxl", "devt", "csin", "s0ds", "s1ds", "s2ds", "s3ds", "smglr", "smgtb",
161 "birep", "binel", "bicr", "colornm", "defbi", "endbi", "setcolor", "slines", "dispc", "smpch",
162 "rmpch", "smsc", "rmsc", "pctrm", "scesc", "scesa", "ehhlm", "elhlm", "elohlm", "erhlm",
163 "ethlm", "evhlm", "sgr1", "slength", "OTi2", "OTrs", "OTnl", "OTbs", "OTko", "OTma", "OTG2",
164 "OTG3", "OTG1", "OTG4", "OTGR", "OTGL", "OTGU", "OTGD", "OTGH", "OTGV", "OTGC", "meml", "memu",
165 "box1"];
166
167 fn read_le_u16(r: &mut io::Read) -> io::Result<u16> {
168 let mut b = [0; 2];
169 let mut amt = 0;
170 while amt < b.len() {
171 match r.read(&mut b[amt..])? {
172 0 => return Err(io::Error::new(io::ErrorKind::Other, "end of file")),
173 n => amt += n,
174 }
175 }
176 Ok((b[0] as u16) | ((b[1] as u16) << 8))
177 }
178
179 fn read_byte(r: &mut io::Read) -> io::Result<u8> {
180 match r.bytes().next() {
181 Some(s) => s,
182 None => Err(io::Error::new(io::ErrorKind::Other, "end of file")),
183 }
184 }
185
186 /// Parse a compiled terminfo entry, using long capability names if `longnames`
187 /// is true
188 pub fn parse(file: &mut io::Read, longnames: bool) -> Result<TermInfo, String> {
189 macro_rules! t( ($e:expr) => (
190 match $e {
191 Ok(e) => e,
192 Err(e) => return Err(format!("{}", e))
193 }
194 ) );
195
196 let (bnames, snames, nnames) = if longnames {
197 (boolfnames, stringfnames, numfnames)
198 } else {
199 (boolnames, stringnames, numnames)
200 };
201
202 // Check magic number
203 let magic = t!(read_le_u16(file));
204 if magic != 0x011A {
205 return Err(format!("invalid magic number: expected {:x}, found {:x}",
206 0x011A,
207 magic));
208 }
209
210 // According to the spec, these fields must be >= -1 where -1 means that the feature is not
211 // supported. Using 0 instead of -1 works because we skip sections with length 0.
212 macro_rules! read_nonneg {
213 () => {{
214 match t!(read_le_u16(file)) as i16 {
215 n if n >= 0 => n as usize,
216 -1 => 0,
217 _ => return Err("incompatible file: length fields must be >= -1".to_string()),
218 }
219 }}
220 }
221
222 let names_bytes = read_nonneg!();
223 let bools_bytes = read_nonneg!();
224 let numbers_count = read_nonneg!();
225 let string_offsets_count = read_nonneg!();
226 let string_table_bytes = read_nonneg!();
227
228 if names_bytes == 0 {
229 return Err("incompatible file: names field must be at least 1 byte wide".to_string());
230 }
231
232 if bools_bytes > boolnames.len() {
233 return Err("incompatible file: more booleans than expected".to_string());
234 }
235
236 if numbers_count > numnames.len() {
237 return Err("incompatible file: more numbers than expected".to_string());
238 }
239
240 if string_offsets_count > stringnames.len() {
241 return Err("incompatible file: more string offsets than expected".to_string());
242 }
243
244 // don't read NUL
245 let mut bytes = Vec::new();
246 t!(file.take((names_bytes - 1) as u64).read_to_end(&mut bytes));
247 let names_str = match String::from_utf8(bytes) {
248 Ok(s) => s,
249 Err(_) => return Err("input not utf-8".to_string()),
250 };
251
252 let term_names: Vec<String> = names_str.split('|')
253 .map(|s| s.to_string())
254 .collect();
255 // consume NUL
256 if t!(read_byte(file)) != b'\0' {
257 return Err("incompatible file: missing null terminator for names section".to_string());
258 }
259
260 let bools_map: HashMap<String, bool> = t! {
261 (0..bools_bytes).filter_map(|i| match read_byte(file) {
262 Err(e) => Some(Err(e)),
263 Ok(1) => Some(Ok((bnames[i].to_string(), true))),
264 Ok(_) => None
265 }).collect()
266 };
267
268 if (bools_bytes + names_bytes) % 2 == 1 {
269 t!(read_byte(file)); // compensate for padding
270 }
271
272 let numbers_map: HashMap<String, u16> = t! {
273 (0..numbers_count).filter_map(|i| match read_le_u16(file) {
274 Ok(0xFFFF) => None,
275 Ok(n) => Some(Ok((nnames[i].to_string(), n))),
276 Err(e) => Some(Err(e))
277 }).collect()
278 };
279
280 let string_map: HashMap<String, Vec<u8>> = if string_offsets_count > 0 {
281 let string_offsets: Vec<u16> = t!((0..string_offsets_count)
282 .map(|_| read_le_u16(file))
283 .collect());
284
285 let mut string_table = Vec::new();
286 t!(file.take(string_table_bytes as u64).read_to_end(&mut string_table));
287
288 t!(string_offsets.into_iter().enumerate().filter(|&(_, offset)| {
289 // non-entry
290 offset != 0xFFFF
291 }).map(|(i, offset)| {
292 let offset = offset as usize;
293
294 let name = if snames[i] == "_" {
295 stringfnames[i]
296 } else {
297 snames[i]
298 };
299
300 if offset == 0xFFFE {
301 // undocumented: FFFE indicates cap@, which means the capability is not present
302 // unsure if the handling for this is correct
303 return Ok((name.to_string(), Vec::new()));
304 }
305
306 // Find the offset of the NUL we want to go to
307 let nulpos = string_table[offset..string_table_bytes].iter().position(|&b| b == 0);
308 match nulpos {
309 Some(len) => Ok((name.to_string(), string_table[offset..offset + len].to_vec())),
310 None => Err("invalid file: missing NUL in string_table".to_string()),
311 }
312 }).collect())
313 } else {
314 HashMap::new()
315 };
316
317 // And that's all there is to it
318 Ok(TermInfo {
319 names: term_names,
320 bools: bools_map,
321 numbers: numbers_map,
322 strings: string_map,
323 })
324 }
325
326 /// Create a dummy TermInfo struct for msys terminals
327 pub fn msys_terminfo() -> TermInfo {
328 let mut strings = HashMap::new();
329 strings.insert("sgr0".to_string(), b"\x1B[0m".to_vec());
330 strings.insert("bold".to_string(), b"\x1B[1m".to_vec());
331 strings.insert("setaf".to_string(), b"\x1B[3%p1%dm".to_vec());
332 strings.insert("setab".to_string(), b"\x1B[4%p1%dm".to_vec());
333
334 let mut numbers = HashMap::new();
335 numbers.insert("colors".to_string(), 8u16);
336
337 TermInfo {
338 names: vec!["cygwin".to_string()], // msys is a fork of an older cygwin version
339 bools: HashMap::new(),
340 numbers: numbers,
341 strings: strings,
342 }
343 }
344
345 #[cfg(test)]
346 mod test {
347
348 use super::{boolnames, boolfnames, numnames, numfnames, stringnames, stringfnames};
349
350 #[test]
351 fn test_veclens() {
352 assert_eq!(boolfnames.len(), boolnames.len());
353 assert_eq!(numfnames.len(), numnames.len());
354 assert_eq!(stringfnames.len(), stringnames.len());
355 }
356 }