]> git.lizzy.rs Git - rust.git/blob - src/libterm/terminfo/parser/compiled.rs
auto merge of #13211 : csherratt/rust/arc_fix, r=alexcrichton
[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 collections::HashMap;
16 use std::io;
17 use std::str;
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] = &'static["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] = &'static["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] = &'static[ "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] = &'static[ "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] = &'static[ "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] = &'static[ "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 io::Reader,
163              longnames: bool) -> Result<~TermInfo, ~str> {
164     macro_rules! try( ($e:expr) => (
165         match $e { Ok(e) => e, Err(e) => return Err(format!("{}", e)) }
166     ) )
167
168     let bnames;
169     let snames;
170     let nnames;
171
172     if longnames {
173         bnames = boolfnames;
174         snames = stringfnames;
175         nnames = numfnames;
176     } else {
177         bnames = boolnames;
178         snames = stringnames;
179         nnames = numnames;
180     }
181
182     // Check magic number
183     let magic = try!(file.read_le_u16());
184     if magic != 0x011A {
185         return Err(format!("invalid magic number: expected {:x} but found {:x}",
186                            0x011A, magic as uint));
187     }
188
189     let names_bytes          = try!(file.read_le_i16()) as int;
190     let bools_bytes          = try!(file.read_le_i16()) as int;
191     let numbers_count        = try!(file.read_le_i16()) as int;
192     let string_offsets_count = try!(file.read_le_i16()) as int;
193     let string_table_bytes   = try!(file.read_le_i16()) as int;
194
195     assert!(names_bytes          > 0);
196
197     if (bools_bytes as uint) > boolnames.len() {
198         return Err(~"incompatible file: more booleans than expected");
199     }
200
201     if (numbers_count as uint) > numnames.len() {
202         return Err(~"incompatible file: more numbers than expected");
203     }
204
205     if (string_offsets_count as uint) > stringnames.len() {
206         return Err(~"incompatible file: more string offsets than expected");
207     }
208
209     // don't read NUL
210     let bytes = try!(file.read_exact(names_bytes as uint - 1));
211     let names_str = match str::from_utf8_owned(bytes) {
212         Some(s) => s, None => return Err(~"input not utf-8"),
213     };
214
215     let term_names: Vec<~str> = names_str.split('|').map(|s| s.to_owned()).collect();
216
217     try!(file.read_byte()); // consume NUL
218
219     let mut bools_map = HashMap::new();
220     if bools_bytes != 0 {
221         for i in range(0, bools_bytes) {
222             let b = try!(file.read_byte());
223             if b < 0 {
224                 return Err(~"error: expected more bools but hit EOF");
225             } else if b == 1 {
226                 bools_map.insert(bnames[i].to_owned(), true);
227             }
228         }
229     }
230
231     if (bools_bytes + names_bytes) % 2 == 1 {
232         try!(file.read_byte()); // compensate for padding
233     }
234
235     let mut numbers_map = HashMap::new();
236     if numbers_count != 0 {
237         for i in range(0, numbers_count) {
238             let n = try!(file.read_le_u16());
239             if n != 0xFFFF {
240                 numbers_map.insert(nnames[i].to_owned(), n);
241             }
242         }
243     }
244
245     let mut string_map = HashMap::new();
246
247     if string_offsets_count != 0 {
248         let mut string_offsets = Vec::with_capacity(10);
249         for _ in range(0, string_offsets_count) {
250             string_offsets.push(try!(file.read_le_u16()));
251         }
252
253         let string_table = try!(file.read_exact(string_table_bytes as uint));
254
255         if string_table.len() != string_table_bytes as uint {
256             return Err(~"error: hit EOF before end of string table");
257         }
258
259         for (i, v) in string_offsets.iter().enumerate() {
260             let offset = *v;
261             if offset == 0xFFFF { // non-entry
262                 continue;
263             }
264
265             let name = if snames[i] == "_" {
266                 stringfnames[i]
267             } else {
268                 snames[i]
269             };
270
271             if offset == 0xFFFE {
272                 // undocumented: FFFE indicates cap@, which means the capability is not present
273                 // unsure if the handling for this is correct
274                 string_map.insert(name.to_owned(), Vec::new());
275                 continue;
276             }
277
278
279             // Find the offset of the NUL we want to go to
280             let nulpos = string_table.slice(offset as uint, string_table_bytes as uint)
281                 .iter().position(|&b| b == 0);
282             match nulpos {
283                 Some(len) => {
284                     string_map.insert(name.to_owned(),
285                                       Vec::from_slice(
286                                           string_table.slice(offset as uint,
287                                           offset as uint + len)))
288                 },
289                 None => {
290                     return Err(~"invalid file: missing NUL in string_table");
291                 }
292             };
293         }
294     }
295
296     // And that's all there is to it
297     Ok(~TermInfo {names: term_names, bools: bools_map, numbers: numbers_map, strings: string_map })
298 }
299
300 /// Create a dummy TermInfo struct for msys terminals
301 pub fn msys_terminfo() -> ~TermInfo {
302     let mut strings = HashMap::new();
303     strings.insert(~"sgr0", Vec::from_slice(bytes!("\x1b[0m")));
304     strings.insert(~"bold", Vec::from_slice(bytes!("\x1b[1m")));
305     strings.insert(~"setaf", Vec::from_slice(bytes!("\x1b[3%p1%dm")));
306     strings.insert(~"setab", Vec::from_slice(bytes!("\x1b[4%p1%dm")));
307     ~TermInfo {
308         names: vec!(~"cygwin"), // msys is a fork of an older cygwin version
309         bools: HashMap::new(),
310         numbers: HashMap::new(),
311         strings: strings
312     }
313 }
314
315 #[cfg(test)]
316 mod test {
317
318     use super::{boolnames, boolfnames, numnames, numfnames, stringnames, stringfnames};
319
320     #[test]
321     fn test_veclens() {
322         assert_eq!(boolfnames.len(), boolnames.len());
323         assert_eq!(numfnames.len(), numnames.len());
324         assert_eq!(stringfnames.len(), stringnames.len());
325     }
326
327     #[test]
328     #[ignore(reason = "no ncurses on buildbots, needs a bundled terminfo file to test against")]
329     fn test_parse() {
330         // FIXME #6870: Distribute a compiled file in src/tests and test there
331         // parse(io::fs_reader(&p("/usr/share/terminfo/r/rxvt-256color")).unwrap(), false);
332     }
333 }