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