]> git.lizzy.rs Git - rust.git/blob - src/libterm/terminfo/parser/compiled.rs
auto merge of #12924 : Florob/rust/bigint, 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
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     if (bools_bytes as uint) > boolnames.len() {
199         return Err(~"incompatible file: more booleans than expected");
200     }
201
202     if (numbers_count as uint) > numnames.len() {
203         return Err(~"incompatible file: more numbers than expected");
204     }
205
206     if (string_offsets_count as uint) > stringnames.len() {
207         return Err(~"incompatible file: more string offsets than expected");
208     }
209
210     // don't read NUL
211     let bytes = try!(file.read_bytes(names_bytes as uint - 1));
212     let names_str = match str::from_utf8_owned(bytes) {
213         Some(s) => s, None => return Err(~"input not utf-8"),
214     };
215
216     let term_names: ~[~str] = names_str.split('|').map(|s| s.to_owned()).collect();
217
218     try!(file.read_byte()); // consume NUL
219
220     let mut bools_map = HashMap::new();
221     if bools_bytes != 0 {
222         for i in range(0, bools_bytes) {
223             let b = try!(file.read_byte());
224             if b < 0 {
225                 return Err(~"error: expected more bools but hit EOF");
226             } else if b == 1 {
227                 bools_map.insert(bnames[i].to_owned(), true);
228             }
229         }
230     }
231
232     if (bools_bytes + names_bytes) % 2 == 1 {
233         try!(file.read_byte()); // compensate for padding
234     }
235
236     let mut numbers_map = HashMap::new();
237     if numbers_count != 0 {
238         for i in range(0, numbers_count) {
239             let n = try!(file.read_le_u16());
240             if n != 0xFFFF {
241                 numbers_map.insert(nnames[i].to_owned(), n);
242             }
243         }
244     }
245
246     let mut string_map = HashMap::new();
247
248     if string_offsets_count != 0 {
249         let mut string_offsets = vec::with_capacity(10);
250         for _ in range(0, string_offsets_count) {
251             string_offsets.push(try!(file.read_le_u16()));
252         }
253
254         let string_table = try!(file.read_bytes(string_table_bytes as uint));
255
256         if string_table.len() != string_table_bytes as uint {
257             return Err(~"error: hit EOF before end of string table");
258         }
259
260         for (i, v) in string_offsets.iter().enumerate() {
261             let offset = *v;
262             if offset == 0xFFFF { // non-entry
263                 continue;
264             }
265
266             let name = if snames[i] == "_" {
267                 stringfnames[i]
268             } else {
269                 snames[i]
270             };
271
272             if offset == 0xFFFE {
273                 // undocumented: FFFE indicates cap@, which means the capability is not present
274                 // unsure if the handling for this is correct
275                 string_map.insert(name.to_owned(), ~[]);
276                 continue;
277             }
278
279
280             // Find the offset of the NUL we want to go to
281             let nulpos = string_table.slice(offset as uint, string_table_bytes as uint)
282                 .iter().position(|&b| b == 0);
283             match nulpos {
284                 Some(len) => {
285                     string_map.insert(name.to_owned(),
286                                       string_table.slice(offset as uint,
287                                                          offset as uint + len).to_owned())
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", bytes!("\x1b[0m").to_owned());
304     strings.insert(~"bold", bytes!("\x1b[1m").to_owned());
305     strings.insert(~"setaf", bytes!("\x1b[3%p1%dm").to_owned());
306     strings.insert(~"setab", bytes!("\x1b[4%p1%dm").to_owned());
307     ~TermInfo {
308         names: ~[~"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 }