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