(index<- )        ./libterm/terminfo/parm.rs

    git branch:    * master           5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
    modified:    Sat Apr 19 11:22:39 2014
   1  // Copyright 2012 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  //! Parameterized string expansion
  12  
  13  use std::char;
  14  use std::mem::replace;
  15  
  16  #[deriving(Eq)]
  17  enum States {
  18      Nothing,
  19      Percent,
  20      SetVar,
  21      GetVar,
  22      PushParam,
  23      CharConstant,
  24      CharClose,
  25      IntConstant(int),
  26      FormatPattern(Flags, FormatState),
  27      SeekIfElse(int),
  28      SeekIfElsePercent(int),
  29      SeekIfEnd(int),
  30      SeekIfEndPercent(int)
  31  }
  32  
  33  #[deriving(Eq)]
  34  enum FormatState {
  35      FormatStateFlags,
  36      FormatStateWidth,
  37      FormatStatePrecision
  38  }
  39  
  40  /// Types of parameters a capability can use
  41  #[allow(missing_doc)]
  42  #[deriving(Clone)]
  43  pub enum Param {
  44      String(~str),
  45      Number(int)
  46  }
  47  
  48  /// Container for static and dynamic variable arrays
  49  pub struct Variables {
  50      /// Static variables A-Z
  51      sta: [Param, ..26],
  52      /// Dynamic variables a-z
  53      dyn: [Param, ..26]
  54  }
  55  
  56  impl Variables {
  57      /// Return a new zero-initialized Variables
  58      pub fn new() -> Variables {
  59          Variables {
  60              sta: [
  61                  Number(0), Number(0), Number(0), Number(0), Number(0),
  62                  Number(0), Number(0), Number(0), Number(0), Number(0),
  63                  Number(0), Number(0), Number(0), Number(0), Number(0),
  64                  Number(0), Number(0), Number(0), Number(0), Number(0),
  65                  Number(0), Number(0), Number(0), Number(0), Number(0),
  66                  Number(0),
  67              ],
  68              dyn: [
  69                  Number(0), Number(0), Number(0), Number(0), Number(0),
  70                  Number(0), Number(0), Number(0), Number(0), Number(0),
  71                  Number(0), Number(0), Number(0), Number(0), Number(0),
  72                  Number(0), Number(0), Number(0), Number(0), Number(0),
  73                  Number(0), Number(0), Number(0), Number(0), Number(0),
  74                  Number(0),
  75              ],
  76          }
  77      }
  78  }
  79  
  80  /**
  81    Expand a parameterized capability
  82  
  83    # Arguments
  84    * `cap`    - string to expand
  85    * `params` - vector of params for %p1 etc
  86    * `vars`   - Variables struct for %Pa etc
  87  
  88    To be compatible with ncurses, `vars` should be the same between calls to `expand` for
  89    multiple capabilities for the same terminal.
  90    */
  91  pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
  92      -> Result<Vec<u8> , ~str> {
  93      let mut state = Nothing;
  94  
  95      // expanded cap will only rarely be larger than the cap itself
  96      let mut output = Vec::with_capacity(cap.len());
  97  
  98      let mut stackVec<Param> = Vec::new();
  99  
 100      // Copy parameters into a local vector for mutability
 101      let mut mparams = [
 102          Number(0), Number(0), Number(0), Number(0), Number(0),
 103          Number(0), Number(0), Number(0), Number(0),
 104      ];
 105      for (dst, src) in mparams.mut_iter().zip(params.iter()) {
 106          *dst = (*src).clone();
 107      }
 108  
 109      for c in cap.iter().map(|&x| x) {
 110          let cur = c as char;
 111          let mut old_state = state;
 112          match state {
 113              Nothing => {
 114                  if cur == '%' {
 115                      state = Percent;
 116                  } else {
 117                      output.push(c);
 118                  }
 119              },
 120              Percent => {
 121                  match cur {
 122                      '%' => { output.push(c); state = Nothing },
 123                      'c' => if stack.len() > 0 {
 124                          match stack.pop().unwrap() {
 125                              // if c is 0, use 0200 (128) for ncurses compatibility
 126                              Number(c) => output.push(if c == 0 { 128 } else { c } as u8),
 127                              _       => return Err("a non-char was used with %c".to_owned())
 128                          }
 129                      } else { return Err("stack is empty".to_owned()) },
 130                      'p' => state = PushParam,
 131                      'P' => state = SetVar,
 132                      'g' => state = GetVar,
 133                      '\'' => state = CharConstant,
 134                      '{' => state = IntConstant(0),
 135                      'l' => if stack.len() > 0 {
 136                          match stack.pop().unwrap() {
 137                              String(s) => stack.push(Number(s.len() as int)),
 138                              _         => return Err("a non-str was used with %l".to_owned())
 139                          }
 140                      } else { return Err("stack is empty".to_owned()) },
 141                      '+' => if stack.len() > 1 {
 142                          match (stack.pop().unwrap(), stack.pop().unwrap()) {
 143                              (Number(y), Number(x)) => stack.push(Number(x + y)),
 144                              _ => return Err("non-numbers on stack with +".to_owned())
 145                          }
 146                      } else { return Err("stack is empty".to_owned()) },
 147                      '-' => if stack.len() > 1 {
 148                          match (stack.pop().unwrap(), stack.pop().unwrap()) {
 149                              (Number(y), Number(x)) => stack.push(Number(x - y)),
 150                              _ => return Err("non-numbers on stack with -".to_owned())
 151                          }
 152                      } else { return Err("stack is empty".to_owned()) },
 153                      '*' => if stack.len() > 1 {
 154                          match (stack.pop().unwrap(), stack.pop().unwrap()) {
 155                              (Number(y), Number(x)) => stack.push(Number(x * y)),
 156                              _ => return Err("non-numbers on stack with *".to_owned())
 157                          }
 158                      } else { return Err("stack is empty".to_owned()) },
 159                      '/' => if stack.len() > 1 {
 160                          match (stack.pop().unwrap(), stack.pop().unwrap()) {
 161                              (Number(y), Number(x)) => stack.push(Number(x / y)),
 162                              _ => return Err("non-numbers on stack with /".to_owned())
 163                          }
 164                      } else { return Err("stack is empty".to_owned()) },
 165                      'm' => if stack.len() > 1 {
 166                          match (stack.pop().unwrap(), stack.pop().unwrap()) {
 167                              (Number(y), Number(x)) => stack.push(Number(x % y)),
 168                              _ => return Err("non-numbers on stack with %".to_owned())
 169                          }
 170                      } else { return Err("stack is empty".to_owned()) },
 171                      '&' => if stack.len() > 1 {
 172                          match (stack.pop().unwrap(), stack.pop().unwrap()) {
 173                              (Number(y), Number(x)) => stack.push(Number(x & y)),
 174                              _ => return Err("non-numbers on stack with &".to_owned())
 175                          }
 176                      } else { return Err("stack is empty".to_owned()) },
 177                      '|' => if stack.len() > 1 {
 178                          match (stack.pop().unwrap(), stack.pop().unwrap()) {
 179                              (Number(y), Number(x)) => stack.push(Number(x | y)),
 180                              _ => return Err("non-numbers on stack with |".to_owned())
 181                          }
 182                      } else { return Err("stack is empty".to_owned()) },
 183                      '^' => if stack.len() > 1 {
 184                          match (stack.pop().unwrap(), stack.pop().unwrap()) {
 185                              (Number(y), Number(x)) => stack.push(Number(x ^ y)),
 186                              _ => return Err("non-numbers on stack with ^".to_owned())
 187                          }
 188                      } else { return Err("stack is empty".to_owned()) },
 189                      '=' => if stack.len() > 1 {
 190                          match (stack.pop().unwrap(), stack.pop().unwrap()) {
 191                              (Number(y), Number(x)) => stack.push(Number(if x == y { 1 }
 192                                                                          else { 0 })),
 193                              _ => return Err("non-numbers on stack with =".to_owned())
 194                          }
 195                      } else { return Err("stack is empty".to_owned()) },
 196                      '>' => if stack.len() > 1 {
 197                          match (stack.pop().unwrap(), stack.pop().unwrap()) {
 198                              (Number(y), Number(x)) => stack.push(Number(if x > y { 1 }
 199                                                                          else { 0 })),
 200                              _ => return Err("non-numbers on stack with >".to_owned())
 201                          }
 202                      } else { return Err("stack is empty".to_owned()) },
 203                      '<' => if stack.len() > 1 {
 204                          match (stack.pop().unwrap(), stack.pop().unwrap()) {
 205                              (Number(y), Number(x)) => stack.push(Number(if x < y { 1 }
 206                                                                          else { 0 })),
 207                              _ => return Err("non-numbers on stack with <".to_owned())
 208                          }
 209                      } else { return Err("stack is empty".to_owned()) },
 210                      'A' => if stack.len() > 1 {
 211                          match (stack.pop().unwrap(), stack.pop().unwrap()) {
 212                              (Number(0), Number(_)) => stack.push(Number(0)),
 213                              (Number(_), Number(0)) => stack.push(Number(0)),
 214                              (Number(_), Number(_)) => stack.push(Number(1)),
 215                              _ => return Err("non-numbers on stack with logical and".to_owned())
 216                          }
 217                      } else { return Err("stack is empty".to_owned()) },
 218                      'O' => if stack.len() > 1 {
 219                          match (stack.pop().unwrap(), stack.pop().unwrap()) {
 220                              (Number(0), Number(0)) => stack.push(Number(0)),
 221                              (Number(_), Number(_)) => stack.push(Number(1)),
 222                              _ => return Err("non-numbers on stack with logical or".to_owned())
 223                          }
 224                      } else { return Err("stack is empty".to_owned()) },
 225                      '!' => if stack.len() > 0 {
 226                          match stack.pop().unwrap() {
 227                              Number(0) => stack.push(Number(1)),
 228                              Number(_) => stack.push(Number(0)),
 229                              _ => return Err("non-number on stack with logical not".to_owned())
 230                          }
 231                      } else { return Err("stack is empty".to_owned()) },
 232                      '~' => if stack.len() > 0 {
 233                          match stack.pop().unwrap() {
 234                              Number(x) => stack.push(Number(!x)),
 235                              _         => return Err("non-number on stack with %~".to_owned())
 236                          }
 237                      } else { return Err("stack is empty".to_owned()) },
 238                      'i' => match (mparams[0].clone(), mparams[1].clone()) {
 239                          (Number(x), Number(y)) => {
 240                              mparams[0] = Number(x+1);
 241                              mparams[1] = Number(y+1);
 242                          },
 243                          (_, _) => return Err("first two params not numbers with %i".to_owned())
 244                      },
 245  
 246                      // printf-style support for %doxXs
 247                      'd'|'o'|'x'|'X'|'s' => if stack.len() > 0 {
 248                          let flags = Flags::new();
 249                          let res = format(stack.pop().unwrap(), FormatOp::from_char(cur), flags);
 250                          if res.is_err() { return res }
 251                          output.push_all(res.unwrap().as_slice())
 252                      } else { return Err("stack is empty".to_owned()) },
 253                      ':'|'#'|' '|'.'|'0'..'9' => {
 254                          let mut flags = Flags::new();
 255                          let mut fstate = FormatStateFlags;
 256                          match cur {
 257                              ':' => (),
 258                              '#' => flags.alternate = true,
 259                              ' ' => flags.space = true,
 260                              '.' => fstate = FormatStatePrecision,
 261                              '0'..'9' => {
 262                                  flags.width = cur as uint - '0' as uint;
 263                                  fstate = FormatStateWidth;
 264                              }
 265                              _ => unreachable!()
 266                          }
 267                          state = FormatPattern(flags, fstate);
 268                      }
 269  
 270                      // conditionals
 271                      '?' => (),
 272                      't' => if stack.len() > 0 {
 273                          match stack.pop().unwrap() {
 274                              Number(0) => state = SeekIfElse(0),
 275                              Number(_) => (),
 276                              _         => return Err("non-number on stack \
 277                                                      with conditional".to_owned())
 278                          }
 279                      } else { return Err("stack is empty".to_owned()) },
 280                      'e' => state = SeekIfEnd(0),
 281                      ';' => (),
 282  
 283                      _ => return Err(format!("unrecognized format option {}", cur))
 284                  }
 285              },
 286              PushParam => {
 287                  // params are 1-indexed
 288                  stack.push(mparams[match char::to_digit(cur, 10) {
 289                      Some(d) => d - 1,
 290                      None => return Err("bad param number".to_owned())
 291                  }].clone());
 292              },
 293              SetVar => {
 294                  if cur >= 'A' && cur <= 'Z' {
 295                      if stack.len() > 0 {
 296                          let idx = (cur as u8) - ('A' as u8);
 297                          vars.sta[idx as uint] = stack.pop().unwrap();
 298                      } else { return Err("stack is empty".to_owned()) }
 299                  } else if cur >= 'a' && cur <= 'z' {
 300                      if stack.len() > 0 {
 301                          let idx = (cur as u8) - ('a' as u8);
 302                          vars.dyn[idx as uint] = stack.pop().unwrap();
 303                      } else { return Err("stack is empty".to_owned()) }
 304                  } else {
 305                      return Err("bad variable name in %P".to_owned());
 306                  }
 307              },
 308              GetVar => {
 309                  if cur >= 'A' && cur <= 'Z' {
 310                      let idx = (cur as u8) - ('A' as u8);
 311                      stack.push(vars.sta[idx as uint].clone());
 312                  } else if cur >= 'a' && cur <= 'z' {
 313                      let idx = (cur as u8) - ('a' as u8);
 314                      stack.push(vars.dyn[idx as uint].clone());
 315                  } else {
 316                      return Err("bad variable name in %g".to_owned());
 317                  }
 318              },
 319              CharConstant => {
 320                  stack.push(Number(c as int));
 321                  state = CharClose;
 322              },
 323              CharClose => {
 324                  if cur != '\'' {
 325                      return Err("malformed character constant".to_owned());
 326                  }
 327              },
 328              IntConstant(i) => {
 329                  match cur {
 330                      '}' => {
 331                          stack.push(Number(i));
 332                          state = Nothing;
 333                      }
 334                      '0'..'9' => {
 335                          state = IntConstant(i*10 + (cur as int - '0' as int));
 336                          old_state = Nothing;
 337                      }
 338                      _ => return Err("bad int constant".to_owned())
 339                  }
 340              }
 341              FormatPattern(ref mut flags, ref mut fstate) => {
 342                  old_state = Nothing;
 343                  match (*fstate, cur) {
 344                      (_,'d')|(_,'o')|(_,'x')|(_,'X')|(_,'s') => if stack.len() > 0 {
 345                          let res = format(stack.pop().unwrap(), FormatOp::from_char(cur), *flags);
 346                          if res.is_err() { return res }
 347                          output.push_all(res.unwrap().as_slice());
 348                          old_state = state; // will cause state to go to Nothing
 349                      } else { return Err("stack is empty".to_owned()) },
 350                      (FormatStateFlags,'#') => {
 351                          flags.alternate = true;
 352                      }
 353                      (FormatStateFlags,'-') => {
 354                          flags.left = true;
 355                      }
 356                      (FormatStateFlags,'+') => {
 357                          flags.sign = true;
 358                      }
 359                      (FormatStateFlags,' ') => {
 360                          flags.space = true;
 361                      }
 362                      (FormatStateFlags,'0'..'9') => {
 363                          flags.width = cur as uint - '0' as uint;
 364                          *fstate = FormatStateWidth;
 365                      }
 366                      (FormatStateFlags,'.') => {
 367                          *fstate = FormatStatePrecision;
 368                      }
 369                      (FormatStateWidth,'0'..'9') => {
 370                          let old = flags.width;
 371                          flags.width = flags.width * 10 + (cur as uint - '0' as uint);
 372                          if flags.width < old { return Err("format width overflow".to_owned()) }
 373                      }
 374                      (FormatStateWidth,'.') => {
 375                          *fstate = FormatStatePrecision;
 376                      }
 377                      (FormatStatePrecision,'0'..'9') => {
 378                          let old = flags.precision;
 379                          flags.precision = flags.precision * 10 + (cur as uint - '0' as uint);
 380                          if flags.precision < old {
 381                              return Err("format precision overflow".to_owned())
 382                          }
 383                      }
 384                      _ => return Err("invalid format specifier".to_owned())
 385                  }
 386              }
 387              SeekIfElse(level) => {
 388                  if cur == '%' {
 389                      state = SeekIfElsePercent(level);
 390                  }
 391                  old_state = Nothing;
 392              }
 393              SeekIfElsePercent(level) => {
 394                  if cur == ';' {
 395                      if level == 0 {
 396                          state = Nothing;
 397                      } else {
 398                          state = SeekIfElse(level-1);
 399                      }
 400                  } else if cur == 'e' && level == 0 {
 401                      state = Nothing;
 402                  } else if cur == '?' {
 403                      state = SeekIfElse(level+1);
 404                  } else {
 405                      state = SeekIfElse(level);
 406                  }
 407              }
 408              SeekIfEnd(level) => {
 409                  if cur == '%' {
 410                      state = SeekIfEndPercent(level);
 411                  }
 412                  old_state = Nothing;
 413              }
 414              SeekIfEndPercent(level) => {
 415                  if cur == ';' {
 416                      if level == 0 {
 417                          state = Nothing;
 418                      } else {
 419                          state = SeekIfEnd(level-1);
 420                      }
 421                  } else if cur == '?' {
 422                      state = SeekIfEnd(level+1);
 423                  } else {
 424                      state = SeekIfEnd(level);
 425                  }
 426              }
 427          }
 428          if state == old_state {
 429              state = Nothing;
 430          }
 431      }
 432      Ok(output)
 433  }
 434  
 435  #[deriving(Eq)]
 436  struct Flags {
 437      width: uint,
 438      precision: uint,
 439      alternate: bool,
 440      left: bool,
 441      sign: bool,
 442      space: bool
 443  }
 444  
 445  impl Flags {
 446      fn new() -> Flags {
 447          Flags{ width: 0, precision: 0, alternate: false,
 448                 left: false, sign: false, space: false }
 449      }
 450  }
 451  
 452  enum FormatOp {
 453      FormatDigit,
 454      FormatOctal,
 455      FormatHex,
 456      FormatHEX,
 457      FormatString
 458  }
 459  
 460  impl FormatOp {
 461      fn from_char(cchar) -> FormatOp {
 462          match c {
 463              'd' => FormatDigit,
 464              'o' => FormatOctal,
 465              'x' => FormatHex,
 466              'X' => FormatHEX,
 467              's' => FormatString,
 468              _ => fail!("bad FormatOp char")
 469          }
 470      }
 471      fn to_char(self) -> char {
 472          match self {
 473              FormatDigit => 'd',
 474              FormatOctal => 'o',
 475              FormatHex => 'x',
 476              FormatHEX => 'X',
 477              FormatString => 's'
 478          }
 479      }
 480  }
 481  
 482  fn format(valParam, opFormatOp, flagsFlags) -> Result<Vec<u8> ,~str> {
 483      let mut s = match val {
 484          Number(d) => {
 485              let s = match (op, flags.sign) {
 486                  (FormatDigit, true)  => format!("{:+d}", d).into_bytes(),
 487                  (FormatDigit, false) => format!("{:d}", d).into_bytes(),
 488                  (FormatOctal, _)     => format!("{:o}", d).into_bytes(),
 489                  (FormatHex, _)       => format!("{:x}", d).into_bytes(),
 490                  (FormatHEX, _)       => format!("{:X}", d).into_bytes(),
 491                  (FormatString, _)    => return Err("non-number on stack with %s".to_owned()),
 492              };
 493              let mut sVec<u8> = s.move_iter().collect();
 494              if flags.precision > s.len() {
 495                  let mut s_ = Vec::with_capacity(flags.precision);
 496                  let n = flags.precision - s.len();
 497                  s_.grow(n, &('0' as u8));
 498                  s_.push_all_move(s);
 499                  s = s_;
 500              }
 501              assert!(!s.is_empty(), "string conversion produced empty result");
 502              match op {
 503                  FormatDigit => {
 504                      if flags.space && !(*s.get(0) == '-' as u8 ||
 505                                          *s.get(0) == '+' as u8) {
 506                          s.unshift(' ' as u8);
 507                      }
 508                  }
 509                  FormatOctal => {
 510                      if flags.alternate && *s.get(0) != '0' as u8 {
 511                          s.unshift('0' as u8);
 512                      }
 513                  }
 514                  FormatHex => {
 515                      if flags.alternate {
 516                          let s_ = replace(&mut s, vec!('0' as u8, 'x' as u8));
 517                          s.push_all_move(s_);
 518                      }
 519                  }
 520                  FormatHEX => {
 521                      s = s.as_slice()
 522                           .to_ascii()
 523                           .to_upper()
 524                           .into_bytes()
 525                           .move_iter()
 526                           .collect();
 527                      if flags.alternate {
 528                          let s_ = replace(&mut s, vec!('0' as u8, 'X' as u8));
 529                          s.push_all_move(s_);
 530                      }
 531                  }
 532                  FormatString => unreachable!()
 533              }
 534              s
 535          }
 536          String(s) => {
 537              match op {
 538                  FormatString => {
 539                      let mut s = Vec::from_slice(s.as_bytes());
 540                      if flags.precision > 0 && flags.precision < s.len() {
 541                          s.truncate(flags.precision);
 542                      }
 543                      s
 544                  }
 545                  _ => {
 546                      return Err(format!("non-string on stack with %{}", op.to_char()))
 547                  }
 548              }
 549          }
 550      };
 551      if flags.width > s.len() {
 552          let n = flags.width - s.len();
 553          if flags.left {
 554              s.grow(n, &(' ' as u8));
 555          } else {
 556              let mut s_ = Vec::with_capacity(flags.width);
 557              s_.grow(n, &(' ' as u8));
 558              s_.push_all_move(s);
 559              s = s_;
 560          }
 561      }
 562      Ok(s)
 563  }
 564  
 565  #[cfg(test)]
 566  mod test {
 567      use super::{expand,String,Variables,Number};
 568      use std::result::Ok;
 569  
 570      #[test]
 571      fn test_basic_setabf() {
 572          let s = bytes!("\\E[48;5;%p1%dm");
 573          assert_eq!(expand(s, [Number(1)], &mut Variables::new()).unwrap(),
 574                     bytes!("\\E[48;5;1m").iter().map(|x| *x).collect());
 575      }
 576  
 577      #[test]
 578      fn test_multiple_int_constants() {
 579          assert_eq!(expand(bytes!("%{1}%{2}%d%d"), [], &mut Variables::new()).unwrap(),
 580                     bytes!("21").iter().map(|x| *x).collect());
 581      }
 582  
 583      #[test]
 584      fn test_op_i() {
 585          let mut vars = Variables::new();
 586          assert_eq!(expand(bytes!("%p1%d%p2%d%p3%d%i%p1%d%p2%d%p3%d"),
 587                            [Number(1),Number(2),Number(3)], &mut vars),
 588                     Ok(bytes!("123233").iter().map(|x| *x).collect()));
 589          assert_eq!(expand(bytes!("%p1%d%p2%d%i%p1%d%p2%d"), [], &mut vars),
 590                     Ok(bytes!("0011").iter().map(|x| *x).collect()));
 591      }
 592  
 593      #[test]
 594      fn test_param_stack_failure_conditions() {
 595          let mut varstruct = Variables::new();
 596          let vars = &mut varstruct;
 597          let caps = ["%d", "%c", "%s", "%Pa", "%l", "%!", "%~"];
 598          for cap in caps.iter() {
 599              let res = expand(cap.as_bytes(), [], vars);
 600              assert!(res.is_err(),
 601                      "Op {} succeeded incorrectly with 0 stack entries", *cap);
 602              let p = if *cap == "%s" || *cap == "%l" {
 603                  String("foo".to_owned())
 604              } else {
 605                  Number(97)
 606              };
 607              let res = expand(bytes!("%p1").iter().map(|x| *x).collect::<Vec<_>>()
 608                               .append(cap.as_bytes()).as_slice(),
 609                               [p],
 610                               vars);
 611              assert!(res.is_ok(),
 612                      "Op {} failed with 1 stack entry: {}", *cap, res.unwrap_err());
 613          }
 614          let caps = ["%+", "%-", "%*", "%/", "%m", "%&", "%|", "%A", "%O"];
 615          for cap in caps.iter() {
 616              let res = expand(cap.as_bytes(), [], vars);
 617              assert!(res.is_err(),
 618                      "Binop {} succeeded incorrectly with 0 stack entries", *cap);
 619              let res = expand(bytes!("%{1}").iter().map(|x| *x).collect::<Vec<_>>()
 620                               .append(cap.as_bytes()).as_slice(),
 621                                [],
 622                                vars);
 623              assert!(res.is_err(),
 624                      "Binop {} succeeded incorrectly with 1 stack entry", *cap);
 625              let res = expand(bytes!("%{1}%{2}").iter().map(|x| *x).collect::<Vec<_>>()
 626                               .append(cap.as_bytes()).as_slice(),
 627                               [],
 628                               vars);
 629              assert!(res.is_ok(),
 630                      "Binop {} failed with 2 stack entries: {}", *cap, res.unwrap_err());
 631          }
 632      }
 633  
 634      #[test]
 635      fn test_push_bad_param() {
 636          assert!(expand(bytes!("%pa"), [], &mut Variables::new()).is_err());
 637      }
 638  
 639      #[test]
 640      fn test_comparison_ops() {
 641          let v = [('<', [1u8, 0u8, 0u8]), ('=', [0u8, 1u8, 0u8]), ('>', [0u8, 0u8, 1u8])];
 642          for &(op, bs) in v.iter() {
 643              let s = format!("%\\{1\\}%\\{2\\}%{}%d", op);
 644              let res = expand(s.as_bytes(), [], &mut Variables::new());
 645              assert!(res.is_ok(), res.unwrap_err());
 646              assert_eq!(res.unwrap(), vec!('0' as u8 + bs[0]));
 647              let s = format!("%\\{1\\}%\\{1\\}%{}%d", op);
 648              let res = expand(s.as_bytes(), [], &mut Variables::new());
 649              assert!(res.is_ok(), res.unwrap_err());
 650              assert_eq!(res.unwrap(), vec!('0' as u8 + bs[1]));
 651              let s = format!("%\\{2\\}%\\{1\\}%{}%d", op);
 652              let res = expand(s.as_bytes(), [], &mut Variables::new());
 653              assert!(res.is_ok(), res.unwrap_err());
 654              assert_eq!(res.unwrap(), vec!('0' as u8 + bs[2]));
 655          }
 656      }
 657  
 658      #[test]
 659      fn test_conditionals() {
 660          let mut vars = Variables::new();
 661          let s = bytes!("\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m");
 662          let res = expand(s, [Number(1)], &mut vars);
 663          assert!(res.is_ok(), res.unwrap_err());
 664          assert_eq!(res.unwrap(),
 665                     bytes!("\\E[31m").iter().map(|x| *x).collect());
 666          let res = expand(s, [Number(8)], &mut vars);
 667          assert!(res.is_ok(), res.unwrap_err());
 668          assert_eq!(res.unwrap(),
 669                     bytes!("\\E[90m").iter().map(|x| *x).collect());
 670          let res = expand(s, [Number(42)], &mut vars);
 671          assert!(res.is_ok(), res.unwrap_err());
 672          assert_eq!(res.unwrap(),
 673                     bytes!("\\E[38;5;42m").iter().map(|x| *x).collect());
 674      }
 675  
 676      #[test]
 677      fn test_format() {
 678          let mut varstruct = Variables::new();
 679          let vars = &mut varstruct;
 680          assert_eq!(expand(bytes!("%p1%s%p2%2s%p3%2s%p4%.2s"),
 681                            [String("foo".to_owned()), String("foo".to_owned()),
 682                             String("f".to_owned()), String("foo".to_owned())], vars),
 683                     Ok(bytes!("foofoo ffo").iter().map(|x| *x).collect()));
 684          assert_eq!(expand(bytes!("%p1%:-4.2s"), [String("foo".to_owned())], vars),
 685                     Ok(bytes!("fo  ").iter().map(|x| *x).collect()));
 686  
 687          assert_eq!(expand(bytes!("%p1%d%p1%.3d%p1%5d%p1%:+d"), [Number(1)], vars),
 688                     Ok(bytes!("1001    1+1").iter().map(|x| *x).collect()));
 689          assert_eq!(expand(bytes!("%p1%o%p1%#o%p2%6.4x%p2%#6.4X"), [Number(15), Number(27)], vars),
 690                     Ok(bytes!("17017  001b0X001B").iter()
 691                                                   .map(|x| *x)
 692                                                   .collect()));
 693      }
 694  }


libterm/terminfo/parm.rs:33:16-33:16 -enum- definition:
enum FormatState {
    FormatStateFlags,
    FormatStateWidth,
references:- 4
34: enum FormatState {


libterm/terminfo/parm.rs:435:16-435:16 -struct- definition:
struct Flags {
    width: uint,
    precision: uint,
references:- 12
446:     fn new() -> Flags {
447:         Flags{ width: 0, precision: 0, alternate: false,
448:                left: false, sign: false, space: false }
--
482: fn format(val: Param, op: FormatOp, flags: Flags) -> Result<Vec<u8> ,~str> {
483:     let mut s = match val {


libterm/terminfo/parm.rs:90:5-90:5 -fn- definition:
  */
pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
    -> Result<Vec<u8> , ~str> {
references:- 4
libterm/lib.rs:
215:                 if parm.is_some() {
216:                     let s = expand(parm.unwrap().as_slice(),
217:                                    [],
--
254:         let s = cap.map_or(Err("can't find terminfo capability `sgr0`".to_owned()), |op| {
255:             expand(op.as_slice(), [], &mut Variables::new())
256:         });


libterm/terminfo/parm.rs:451:1-451:1 -enum- definition:
enum FormatOp {
    FormatDigit,
    FormatOctal,
references:- 3
482: fn format(val: Param, op: FormatOp, flags: Flags) -> Result<Vec<u8> ,~str> {
483:     let mut s = match val {


libterm/terminfo/parm.rs:42:19-42:19 -enum- definition:
pub enum Param {
    String(~str),
    Number(int)
references:- 7
90:   */
91: pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
92:     -> Result<Vec<u8> , ~str> {
--
482: fn format(val: Param, op: FormatOp, flags: Flags) -> Result<Vec<u8> ,~str> {
483:     let mut s = match val {


libterm/terminfo/parm.rs:48:53-48:53 -struct- definition:
/// Container for static and dynamic variable arrays
pub struct Variables {
    /// Static variables A-Z
references:- 4
58:     pub fn new() -> Variables {
59:         Variables {
60:             sta: [
--
90:   */
91: pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
92:     -> Result<Vec<u8> , ~str> {


libterm/terminfo/parm.rs:481:1-481:1 -fn- definition:
fn format(val: Param, op: FormatOp, flags: Flags) -> Result<Vec<u8> ,~str> {
    let mut s = match val {
        Number(d) => {
references:- 2
344:                     (_,'d')|(_,'o')|(_,'x')|(_,'X')|(_,'s') => if stack.len() > 0 {
345:                         let res = format(stack.pop().unwrap(), FormatOp::from_char(cur), *flags);
346:                         if res.is_err() { return res }


libterm/terminfo/parm.rs:16:16-16:16 -enum- definition:
enum States {
    Nothing,
    Percent,
references:- 3
17: enum States {