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

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

libextra/terminfo/parm.rs:447:1-447:1 -enum- definition:

enum FormatOp {
references:-
456: impl FormatOp {
478: fn format(val: Param, op: FormatOp, flags: Flags) -> Result<~[u8],~str> {
457:     fn from_char(c: char) -> FormatOp {


libextra/terminfo/parm.rs:41:19-41:19 -enum- definition:
#[deriving(Clone)]
pub enum Param {
references:-
90: pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
50:     sta: [Param, ..26],
97:     let mut stack: ~[Param] = ~[];
41: #[deriving(Clone)]
52:     dyn: [Param, ..26]
478: fn format(val: Param, op: FormatOp, flags: Flags) -> Result<~[u8],~str> {
41: #[deriving(Clone)]


libextra/terminfo/parm.rs:16:16-16:16 -enum- definition:
#[deriving(Eq)]
enum States {
references:-
16: #[deriving(Eq)]
16: #[deriving(Eq)]
16: #[deriving(Eq)]


libextra/terminfo/parm.rs:431:16-431:16 -struct- definition:
#[deriving(Eq)]
struct Flags {
references:-
431: #[deriving(Eq)]
441: impl Flags {
431: #[deriving(Eq)]
442:     fn new() -> Flags {
443:         Flags{ width: 0, precision: 0, alternate: false,
431: #[deriving(Eq)]
431: #[deriving(Eq)]
431: #[deriving(Eq)]
478: fn format(val: Param, op: FormatOp, flags: Flags) -> Result<~[u8],~str> {
431: #[deriving(Eq)]
26:     FormatPattern(Flags, FormatState),
431: #[deriving(Eq)]


libextra/terminfo/parm.rs:89:5-89:5 -fn- definition:
  */
pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
references:-
libextra/term.rs:
144:             let s = expand(*self.ti.strings.find_equiv(&("setaf")).unwrap(),
224:             expand(*op, [], &mut Variables::new())
186:                     let s = expand(*parm.unwrap(), [], &mut Variables::new());
164:             let s = expand(*self.ti.strings.find_equiv(&("setab")).unwrap(),


libextra/terminfo/parm.rs:477:1-477:1 -fn- definition:

fn format(val: Param, op: FormatOp, flags: Flags) -> Result<~[u8],~str> {
references:-
343:                         let res = format(stack.pop(), FormatOp::from_char(cur), *flags);
248:                         let res = format(stack.pop(), FormatOp::from_char(cur), flags);


libextra/terminfo/parm.rs:33:16-33:16 -enum- definition:
#[deriving(Eq)]
enum FormatState {
references:-
33: #[deriving(Eq)]
33: #[deriving(Eq)]
26:     FormatPattern(Flags, FormatState),
33: #[deriving(Eq)]


libextra/terminfo/parm.rs:47:53-47:53 -struct- definition:
/// Container for static and dynamic variable arrays
pub struct Variables {
references:-
55: impl Variables {
58:         Variables {
57:     pub fn new() -> Variables {
90: pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)