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