]> git.lizzy.rs Git - rust.git/blob - src/libterm/terminfo/parser/compiled.rs
rollup merge of #20482: kmcallister/macro-reform
[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_upper_case_globals)]
12
13 //! ncurses-compatible compiled terminfo format parsing (term(5))
14
15 use std::collections::HashMap;
16 use std::io;
17 use super::super::TermInfo;
18
19 // These are the orders ncurses uses in its compiled format (as of 5.9). Not sure if portable.
20
21 pub static boolfnames: &'static[&'static str] = &["auto_left_margin", "auto_right_margin",
22     "no_esc_ctlc", "ceol_standout_glitch", "eat_newline_glitch", "erase_overstrike", "generic_type",
23     "hard_copy", "has_meta_key", "has_status_line", "insert_null_glitch", "memory_above",
24     "memory_below", "move_insert_mode", "move_standout_mode", "over_strike", "status_line_esc_ok",
25     "dest_tabs_magic_smso", "tilde_glitch", "transparent_underline", "xon_xoff", "needs_xon_xoff",
26     "prtr_silent", "hard_cursor", "non_rev_rmcup", "no_pad_char", "non_dest_scroll_region",
27     "can_change", "back_color_erase", "hue_lightness_saturation", "col_addr_glitch",
28     "cr_cancels_micro_mode", "has_print_wheel", "row_addr_glitch", "semi_auto_right_margin",
29     "cpi_changes_res", "lpi_changes_res", "backspaces_with_bs", "crt_no_scrolling",
30     "no_correctly_working_cr", "gnu_has_meta_key", "linefeed_is_newline", "has_hardware_tabs",
31     "return_does_clr_eol"];
32
33 pub static boolnames: &'static[&'static str] = &["bw", "am", "xsb", "xhp", "xenl", "eo",
34     "gn", "hc", "km", "hs", "in", "db", "da", "mir", "msgr", "os", "eslok", "xt", "hz", "ul", "xon",
35     "nxon", "mc5i", "chts", "nrrmc", "npc", "ndscr", "ccc", "bce", "hls", "xhpa", "crxm", "daisy",
36     "xvpa", "sam", "cpix", "lpix", "OTbs", "OTns", "OTnc", "OTMT", "OTNL", "OTpt", "OTxr"];
37
38 pub static numfnames: &'static[&'static str] = &[ "columns", "init_tabs", "lines",
39     "lines_of_memory", "magic_cookie_glitch", "padding_baud_rate", "virtual_terminal",
40     "width_status_line", "num_labels", "label_height", "label_width", "max_attributes",
41     "maximum_windows", "max_colors", "max_pairs", "no_color_video", "buffer_capacity",
42     "dot_vert_spacing", "dot_horz_spacing", "max_micro_address", "max_micro_jump", "micro_col_size",
43     "micro_line_size", "number_of_pins", "output_res_char", "output_res_line",
44     "output_res_horz_inch", "output_res_vert_inch", "print_rate", "wide_char_size", "buttons",
45     "bit_image_entwining", "bit_image_type", "magic_cookie_glitch_ul", "carriage_return_delay",
46     "new_line_delay", "backspace_delay", "horizontal_tab_delay", "number_of_function_keys"];
47
48 pub static numnames: &'static[&'static str] = &[ "cols", "it", "lines", "lm", "xmc", "pb",
49     "vt", "wsl", "nlab", "lh", "lw", "ma", "wnum", "colors", "pairs", "ncv", "bufsz", "spinv",
50     "spinh", "maddr", "mjump", "mcs", "mls", "npins", "orc", "orl", "orhi", "orvi", "cps", "widcs",
51     "btns", "bitwin", "bitype", "UTug", "OTdC", "OTdN", "OTdB", "OTdT", "OTkn"];
52
53 pub static stringfnames: &'static[&'static str] = &[ "back_tab", "bell", "carriage_return",
54     "change_scroll_region", "clear_all_tabs", "clear_screen", "clr_eol", "clr_eos",
55     "column_address", "command_character", "cursor_address", "cursor_down", "cursor_home",
56     "cursor_invisible", "cursor_left", "cursor_mem_address", "cursor_normal", "cursor_right",
57     "cursor_to_ll", "cursor_up", "cursor_visible", "delete_character", "delete_line",
58     "dis_status_line", "down_half_line", "enter_alt_charset_mode", "enter_blink_mode",
59     "enter_bold_mode", "enter_ca_mode", "enter_delete_mode", "enter_dim_mode", "enter_insert_mode",
60     "enter_secure_mode", "enter_protected_mode", "enter_reverse_mode", "enter_standout_mode",
61     "enter_underline_mode", "erase_chars", "exit_alt_charset_mode", "exit_attribute_mode",
62     "exit_ca_mode", "exit_delete_mode", "exit_insert_mode", "exit_standout_mode",
63     "exit_underline_mode", "flash_screen", "form_feed", "from_status_line", "init_1string",
64     "init_2string", "init_3string", "init_file", "insert_character", "insert_line",
65     "insert_padding", "key_backspace", "key_catab", "key_clear", "key_ctab", "key_dc", "key_dl",
66     "key_down", "key_eic", "key_eol", "key_eos", "key_f0", "key_f1", "key_f10", "key_f2", "key_f3",
67     "key_f4", "key_f5", "key_f6", "key_f7", "key_f8", "key_f9", "key_home", "key_ic", "key_il",
68     "key_left", "key_ll", "key_npage", "key_ppage", "key_right", "key_sf", "key_sr", "key_stab",
69     "key_up", "keypad_local", "keypad_xmit", "lab_f0", "lab_f1", "lab_f10", "lab_f2", "lab_f3",
70     "lab_f4", "lab_f5", "lab_f6", "lab_f7", "lab_f8", "lab_f9", "meta_off", "meta_on", "newline",
71     "pad_char", "parm_dch", "parm_delete_line", "parm_down_cursor", "parm_ich", "parm_index",
72     "parm_insert_line", "parm_left_cursor", "parm_right_cursor", "parm_rindex", "parm_up_cursor",
73     "pkey_key", "pkey_local", "pkey_xmit", "print_screen", "prtr_off", "prtr_on", "repeat_char",
74     "reset_1string", "reset_2string", "reset_3string", "reset_file", "restore_cursor",
75     "row_address", "save_cursor", "scroll_forward", "scroll_reverse", "set_attributes", "set_tab",
76     "set_window", "tab", "to_status_line", "underline_char", "up_half_line", "init_prog", "key_a1",
77     "key_a3", "key_b2", "key_c1", "key_c3", "prtr_non", "char_padding", "acs_chars", "plab_norm",
78     "key_btab", "enter_xon_mode", "exit_xon_mode", "enter_am_mode", "exit_am_mode", "xon_character",
79     "xoff_character", "ena_acs", "label_on", "label_off", "key_beg", "key_cancel", "key_close",
80     "key_command", "key_copy", "key_create", "key_end", "key_enter", "key_exit", "key_find",
81     "key_help", "key_mark", "key_message", "key_move", "key_next", "key_open", "key_options",
82     "key_previous", "key_print", "key_redo", "key_reference", "key_refresh", "key_replace",
83     "key_restart", "key_resume", "key_save", "key_suspend", "key_undo", "key_sbeg", "key_scancel",
84     "key_scommand", "key_scopy", "key_screate", "key_sdc", "key_sdl", "key_select", "key_send",
85     "key_seol", "key_sexit", "key_sfind", "key_shelp", "key_shome", "key_sic", "key_sleft",
86     "key_smessage", "key_smove", "key_snext", "key_soptions", "key_sprevious", "key_sprint",
87     "key_sredo", "key_sreplace", "key_sright", "key_srsume", "key_ssave", "key_ssuspend",
88     "key_sundo", "req_for_input", "key_f11", "key_f12", "key_f13", "key_f14", "key_f15", "key_f16",
89     "key_f17", "key_f18", "key_f19", "key_f20", "key_f21", "key_f22", "key_f23", "key_f24",
90     "key_f25", "key_f26", "key_f27", "key_f28", "key_f29", "key_f30", "key_f31", "key_f32",
91     "key_f33", "key_f34", "key_f35", "key_f36", "key_f37", "key_f38", "key_f39", "key_f40",
92     "key_f41", "key_f42", "key_f43", "key_f44", "key_f45", "key_f46", "key_f47", "key_f48",
93     "key_f49", "key_f50", "key_f51", "key_f52", "key_f53", "key_f54", "key_f55", "key_f56",
94     "key_f57", "key_f58", "key_f59", "key_f60", "key_f61", "key_f62", "key_f63", "clr_bol",
95     "clear_margins", "set_left_margin", "set_right_margin", "label_format", "set_clock",
96     "display_clock", "remove_clock", "create_window", "goto_window", "hangup", "dial_phone",
97     "quick_dial", "tone", "pulse", "flash_hook", "fixed_pause", "wait_tone", "user0", "user1",
98     "user2", "user3", "user4", "user5", "user6", "user7", "user8", "user9", "orig_pair",
99     "orig_colors", "initialize_color", "initialize_pair", "set_color_pair", "set_foreground",
100     "set_background", "change_char_pitch", "change_line_pitch", "change_res_horz",
101     "change_res_vert", "define_char", "enter_doublewide_mode", "enter_draft_quality",
102     "enter_italics_mode", "enter_leftward_mode", "enter_micro_mode", "enter_near_letter_quality",
103     "enter_normal_quality", "enter_shadow_mode", "enter_subscript_mode", "enter_superscript_mode",
104     "enter_upward_mode", "exit_doublewide_mode", "exit_italics_mode", "exit_leftward_mode",
105     "exit_micro_mode", "exit_shadow_mode", "exit_subscript_mode", "exit_superscript_mode",
106     "exit_upward_mode", "micro_column_address", "micro_down", "micro_left", "micro_right",
107     "micro_row_address", "micro_up", "order_of_pins", "parm_down_micro", "parm_left_micro",
108     "parm_right_micro", "parm_up_micro", "select_char_set", "set_bottom_margin",
109     "set_bottom_margin_parm", "set_left_margin_parm", "set_right_margin_parm", "set_top_margin",
110     "set_top_margin_parm", "start_bit_image", "start_char_set_def", "stop_bit_image",
111     "stop_char_set_def", "subscript_characters", "superscript_characters", "these_cause_cr",
112     "zero_motion", "char_set_names", "key_mouse", "mouse_info", "req_mouse_pos", "get_mouse",
113     "set_a_foreground", "set_a_background", "pkey_plab", "device_type", "code_set_init",
114     "set0_des_seq", "set1_des_seq", "set2_des_seq", "set3_des_seq", "set_lr_margin",
115     "set_tb_margin", "bit_image_repeat", "bit_image_newline", "bit_image_carriage_return",
116     "color_names", "define_bit_image_region", "end_bit_image_region", "set_color_band",
117     "set_page_length", "display_pc_char", "enter_pc_charset_mode", "exit_pc_charset_mode",
118     "enter_scancode_mode", "exit_scancode_mode", "pc_term_options", "scancode_escape",
119     "alt_scancode_esc", "enter_horizontal_hl_mode", "enter_left_hl_mode", "enter_low_hl_mode",
120     "enter_right_hl_mode", "enter_top_hl_mode", "enter_vertical_hl_mode", "set_a_attributes",
121     "set_pglen_inch", "termcap_init2", "termcap_reset", "linefeed_if_not_lf", "backspace_if_not_bs",
122     "other_non_function_keys", "arrow_key_map", "acs_ulcorner", "acs_llcorner", "acs_urcorner",
123     "acs_lrcorner", "acs_ltee", "acs_rtee", "acs_btee", "acs_ttee", "acs_hline", "acs_vline",
124     "acs_plus", "memory_lock", "memory_unlock", "box_chars_1"];
125
126 pub static stringnames: &'static[&'static str] = &[ "cbt", "_", "cr", "csr", "tbc", "clear",
127     "_", "_", "hpa", "cmdch", "cup", "cud1", "home", "civis", "cub1", "mrcup", "cnorm", "cuf1",
128     "ll", "cuu1", "cvvis", "dch1", "dl1", "dsl", "hd", "smacs", "blink", "bold", "smcup", "smdc",
129     "dim", "smir", "invis", "prot", "rev", "smso", "smul", "ech", "rmacs", "sgr0", "rmcup", "rmdc",
130     "rmir", "rmso", "rmul", "flash", "ff", "fsl", "is1", "is2", "is3", "if", "ich1", "il1", "ip",
131     "kbs", "ktbc", "kclr", "kctab", "_", "_", "kcud1", "_", "_", "_", "_", "_", "_", "_", "_", "_",
132     "_", "_", "_", "_", "_", "khome", "_", "_", "kcub1", "_", "knp", "kpp", "kcuf1", "_", "_",
133     "khts", "_", "rmkx", "smkx", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "rmm", "_",
134     "_", "pad", "dch", "dl", "cud", "ich", "indn", "il", "cub", "cuf", "rin", "cuu", "pfkey",
135     "pfloc", "pfx", "mc0", "mc4", "_", "rep", "rs1", "rs2", "rs3", "rf", "rc", "vpa", "sc", "ind",
136     "ri", "sgr", "_", "wind", "_", "tsl", "uc", "hu", "iprog", "_", "_", "_", "_", "_", "mc5p",
137     "rmp", "acsc", "pln", "kcbt", "smxon", "rmxon", "smam", "rmam", "xonc", "xoffc", "_", "smln",
138     "rmln", "_", "kcan", "kclo", "kcmd", "kcpy", "kcrt", "_", "kent", "kext", "kfnd", "khlp",
139     "kmrk", "kmsg", "kmov", "knxt", "kopn", "kopt", "kprv", "kprt", "krdo", "kref", "krfr", "krpl",
140     "krst", "kres", "ksav", "kspd", "kund", "kBEG", "kCAN", "kCMD", "kCPY", "kCRT", "_", "_",
141     "kslt", "kEND", "kEOL", "kEXT", "kFND", "kHLP", "kHOM", "_", "kLFT", "kMSG", "kMOV", "kNXT",
142     "kOPT", "kPRV", "kPRT", "kRDO", "kRPL", "kRIT", "kRES", "kSAV", "kSPD", "kUND", "rfi", "_", "_",
143     "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_",
144     "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_",
145     "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_",
146     "dclk", "rmclk", "cwin", "wingo", "_", "dial", "qdial", "_", "_", "hook", "pause", "wait", "_",
147     "_", "_", "_", "_", "_", "_", "_", "_", "_", "op", "oc", "initc", "initp", "scp", "setf",
148     "setb", "cpi", "lpi", "chr", "cvr", "defc", "swidm", "sdrfq", "sitm", "slm", "smicm", "snlq",
149     "snrmq", "sshm", "ssubm", "ssupm", "sum", "rwidm", "ritm", "rlm", "rmicm", "rshm", "rsubm",
150     "rsupm", "rum", "mhpa", "mcud1", "mcub1", "mcuf1", "mvpa", "mcuu1", "porder", "mcud", "mcub",
151     "mcuf", "mcuu", "scs", "smgb", "smgbp", "smglp", "smgrp", "smgt", "smgtp", "sbim", "scsd",
152     "rbim", "rcsd", "subcs", "supcs", "docr", "zerom", "csnm", "kmous", "minfo", "reqmp", "getm",
153     "setaf", "setab", "pfxl", "devt", "csin", "s0ds", "s1ds", "s2ds", "s3ds", "smglr", "smgtb",
154     "birep", "binel", "bicr", "colornm", "defbi", "endbi", "setcolor", "slines", "dispc", "smpch",
155     "rmpch", "smsc", "rmsc", "pctrm", "scesc", "scesa", "ehhlm", "elhlm", "elohlm", "erhlm",
156     "ethlm", "evhlm", "sgr1", "slength", "OTi2", "OTrs", "OTnl", "OTbs", "OTko", "OTma", "OTG2",
157     "OTG3", "OTG1", "OTG4", "OTGR", "OTGL", "OTGU", "OTGD", "OTGH", "OTGV", "OTGC", "meml", "memu",
158     "box1"];
159
160 /// Parse a compiled terminfo entry, using long capability names if `longnames` is true
161 pub fn parse(file: &mut io::Reader, longnames: bool)
162              -> Result<Box<TermInfo>, String> {
163     macro_rules! try { ($e:expr) => (
164         match $e {
165             Ok(e) => e,
166             Err(e) => return Err(format!("{}", e))
167         }
168     ) }
169
170     let bnames;
171     let snames;
172     let nnames;
173
174     if longnames {
175         bnames = boolfnames;
176         snames = stringfnames;
177         nnames = numfnames;
178     } else {
179         bnames = boolnames;
180         snames = stringnames;
181         nnames = numnames;
182     }
183
184     // Check magic number
185     let magic = try!(file.read_le_u16());
186     if magic != 0x011A {
187         return Err(format!("invalid magic number: expected {:x}, found {:x}",
188                            0x011Au, magic as uint));
189     }
190
191     let names_bytes          = try!(file.read_le_i16()) as int;
192     let bools_bytes          = try!(file.read_le_i16()) as int;
193     let numbers_count        = try!(file.read_le_i16()) as int;
194     let string_offsets_count = try!(file.read_le_i16()) as int;
195     let string_table_bytes   = try!(file.read_le_i16()) as int;
196
197     assert!(names_bytes          > 0);
198
199     if (bools_bytes as uint) > boolnames.len() {
200         return Err("incompatible file: more booleans than \
201                     expected".to_string());
202     }
203
204     if (numbers_count as uint) > numnames.len() {
205         return Err("incompatible file: more numbers than \
206                     expected".to_string());
207     }
208
209     if (string_offsets_count as uint) > stringnames.len() {
210         return Err("incompatible file: more string offsets than \
211                     expected".to_string());
212     }
213
214     // don't read NUL
215     let bytes = try!(file.read_exact(names_bytes as uint - 1));
216     let names_str = match String::from_utf8(bytes) {
217         Ok(s)  => s,
218         Err(_) => return Err("input not utf-8".to_string()),
219     };
220
221     let term_names: Vec<String> = names_str.split('|')
222                                            .map(|s| s.to_string())
223                                            .collect();
224
225     try!(file.read_byte()); // consume NUL
226
227     let mut bools_map = HashMap::new();
228     if bools_bytes != 0 {
229         for i in range(0, bools_bytes) {
230             let b = try!(file.read_byte());
231             if b == 1 {
232                 bools_map.insert(bnames[i as uint].to_string(), true);
233             }
234         }
235     }
236
237     if (bools_bytes + names_bytes) % 2 == 1 {
238         try!(file.read_byte()); // compensate for padding
239     }
240
241     let mut numbers_map = HashMap::new();
242     if numbers_count != 0 {
243         for i in range(0, numbers_count) {
244             let n = try!(file.read_le_u16());
245             if n != 0xFFFF {
246                 numbers_map.insert(nnames[i as uint].to_string(), n);
247             }
248         }
249     }
250
251     let mut string_map = HashMap::new();
252
253     if string_offsets_count != 0 {
254         let mut string_offsets = Vec::with_capacity(10);
255         for _ in range(0, string_offsets_count) {
256             string_offsets.push(try!(file.read_le_u16()));
257         }
258
259         let string_table = try!(file.read_exact(string_table_bytes as uint));
260
261         if string_table.len() != string_table_bytes as uint {
262             return Err("error: hit EOF before end of string \
263                         table".to_string());
264         }
265
266         for (i, v) in string_offsets.iter().enumerate() {
267             let offset = *v;
268             if offset == 0xFFFF { // non-entry
269                 continue;
270             }
271
272             let name = if snames[i] == "_" {
273                 stringfnames[i]
274             } else {
275                 snames[i]
276             };
277
278             if offset == 0xFFFE {
279                 // undocumented: FFFE indicates cap@, which means the capability is not present
280                 // unsure if the handling for this is correct
281                 string_map.insert(name.to_string(), Vec::new());
282                 continue;
283             }
284
285
286             // Find the offset of the NUL we want to go to
287             let nulpos = string_table[offset as uint .. string_table_bytes as uint]
288                 .iter().position(|&b| b == 0);
289             match nulpos {
290                 Some(len) => {
291                     string_map.insert(name.to_string(),
292                                       string_table[offset as uint ..
293                                           offset as uint + len].to_vec())
294                 },
295                 None => {
296                     return Err("invalid file: missing NUL in \
297                                 string_table".to_string());
298                 }
299             };
300         }
301     }
302
303     // And that's all there is to it
304     Ok(box TermInfo {
305         names: term_names,
306         bools: bools_map,
307         numbers: numbers_map,
308         strings: string_map
309     })
310 }
311
312 /// Create a dummy TermInfo struct for msys terminals
313 pub fn msys_terminfo() -> Box<TermInfo> {
314     let mut strings = HashMap::new();
315     strings.insert("sgr0".to_string(), b"\x1B[0m".to_vec());
316     strings.insert("bold".to_string(), b"\x1B[1m".to_vec());
317     strings.insert("setaf".to_string(), b"\x1B[3%p1%dm".to_vec());
318     strings.insert("setab".to_string(), b"\x1B[4%p1%dm".to_vec());
319     box TermInfo {
320         names: vec!("cygwin".to_string()), // msys is a fork of an older cygwin version
321         bools: HashMap::new(),
322         numbers: HashMap::new(),
323         strings: strings
324     }
325 }
326
327 #[cfg(test)]
328 mod test {
329
330     use super::{boolnames, boolfnames, numnames, numfnames, stringnames, stringfnames};
331
332     #[test]
333     fn test_veclens() {
334         assert_eq!(boolfnames.len(), boolnames.len());
335         assert_eq!(numfnames.len(), numnames.len());
336         assert_eq!(stringfnames.len(), stringnames.len());
337     }
338
339     #[test]
340     #[ignore(reason = "no ncurses on buildbots, needs a bundled terminfo file to test against")]
341     fn test_parse() {
342         // FIXME #6870: Distribute a compiled file in src/tests and test there
343         // parse(io::fs_reader(&p("/usr/share/terminfo/r/rxvt-256color")).unwrap(), false);
344     }
345 }