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