(index<- )        ./libterm/terminfo/parser/compiled.rs

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