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