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