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