]>
git.proxmox.com Git - rustc.git/blob - src/libterm/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.
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.
11 //! Parameterized string expansion
15 use self::FormatState
::*;
16 use self::FormatOp
::*;
18 use std
::iter
::repeat
;
20 #[derive(Clone, Copy, PartialEq)]
30 FormatPattern(Flags
, FormatState
),
32 SeekIfElsePercent(usize),
34 SeekIfEndPercent(usize),
37 #[derive(Copy, PartialEq, Clone)]
44 /// Types of parameters a capability can use
45 #[allow(missing_docs)]
52 /// Container for static and dynamic variable arrays
53 pub struct Variables
{
54 /// Static variables A-Z
56 /// Dynamic variables a-z
61 /// Return a new zero-initialized Variables
62 pub fn new() -> Variables
{
64 sta
: [Number(0), Number(0), Number(0), Number(0), Number(0), Number(0), Number(0),
65 Number(0), Number(0), Number(0), Number(0), Number(0), Number(0), Number(0),
66 Number(0), Number(0), Number(0), Number(0), Number(0), Number(0), Number(0),
67 Number(0), Number(0), Number(0), Number(0), Number(0)],
68 dyn: [Number(0), Number(0), Number(0), Number(0), Number(0), Number(0), Number(0),
69 Number(0), Number(0), Number(0), Number(0), Number(0), Number(0), Number(0),
70 Number(0), Number(0), Number(0), Number(0), Number(0), Number(0), Number(0),
71 Number(0), Number(0), Number(0), Number(0), Number(0)],
76 /// Expand a parameterized capability
79 /// * `cap` - string to expand
80 /// * `params` - vector of params for %p1 etc
81 /// * `vars` - Variables struct for %Pa etc
83 /// To be compatible with ncurses, `vars` should be the same between calls to `expand` for
84 /// multiple capabilities for the same terminal.
85 pub fn expand(cap
: &[u8], params
: &[Param
], vars
: &mut Variables
) -> Result
<Vec
<u8>, String
> {
86 let mut state
= Nothing
;
88 // expanded cap will only rarely be larger than the cap itself
89 let mut output
= Vec
::with_capacity(cap
.len());
91 let mut stack
: Vec
<Param
> = Vec
::new();
93 // Copy parameters into a local vector for mutability
94 let mut mparams
= [Number(0), Number(0), Number(0), Number(0), Number(0), Number(0),
95 Number(0), Number(0), Number(0)];
96 for (dst
, src
) in mparams
.iter_mut().zip(params
.iter()) {
97 *dst
= (*src
).clone();
100 for &c
in cap
.iter() {
102 let mut old_state
= state
;
119 // if c is 0, use 0200 (128) for ncurses compatibility
120 Some(Number(0)) => output
.push(128u8),
121 // Don't check bounds. ncurses just casts and truncates.
122 Some(Number(c
)) => output
.push(c
as u8),
123 Some(_
) => return Err("a non-char was used with %c".to_string()),
124 None
=> return Err("stack is empty".to_string()),
127 'p'
=> state
= PushParam
,
128 'P'
=> state
= SetVar
,
129 'g'
=> state
= GetVar
,
130 '
\''
=> state
= CharConstant
,
131 '
{'
=> state
= IntConstant(0),
134 Some(Words(s
)) => stack
.push(Number(s
.len() as i32)),
135 Some(_
) => return Err("a non-str was used with %l".to_string()),
136 None
=> return Err("stack is empty".to_string()),
139 '
+'
| '
-'
| '
/'
| '
*'
| '
^' | '
&'
| '
|'
| 'm'
=> {
140 match (stack
.pop(), stack
.pop()) {
141 (Some(Number(y
)), Some(Number(x
))) => {
142 stack
.push(Number(match cur
{
151 _
=> unreachable
!("All cases handled"),
154 (Some(_
), Some(_
)) => {
155 return Err(format
!("non-numbers on stack with {}", cur
))
157 _
=> return Err("stack is empty".to_string()),
160 '
='
| '
>'
| '
<'
| 'A'
| 'O'
=> {
161 match (stack
.pop(), stack
.pop()) {
162 (Some(Number(y
)), Some(Number(x
))) => {
163 stack
.push(Number(if match cur
{
167 'A'
=> x
> 0 && y
> 0,
168 'O'
=> x
> 0 || y
> 0,
176 (Some(_
), Some(_
)) => {
177 return Err(format
!("non-numbers on stack with {}", cur
))
179 _
=> return Err("stack is empty".to_string()),
185 stack
.push(Number(match cur
{
192 Some(_
) => return Err(format
!("non-numbers on stack with {}", cur
)),
193 None
=> return Err("stack is empty".to_string()),
197 match (&mparams
[0], &mparams
[1]) {
198 (&Number(x
), &Number(y
)) => {
199 mparams
[0] = Number(x
+ 1);
200 mparams
[1] = Number(y
+ 1);
203 return Err("first two params not numbers with %i".to_string())
208 // printf-style support for %doxXs
209 'd'
| 'o'
| 'x'
| 'X'
| 's'
=> {
210 if let Some(arg
) = stack
.pop() {
211 let flags
= Flags
::new();
212 let res
= format(arg
, FormatOp
::from_char(cur
), flags
)?
;
213 output
.extend(res
.iter().map(|x
| *x
));
215 return Err("stack is empty".to_string());
218 '
:'
| '
#' | ' ' | '.' | '0'...'9' => {
219 let mut flags
= Flags
::new();
220 let mut fstate
= FormatStateFlags
;
223 '
#' => flags.alternate = true,
224 ' '
=> flags
.space
= true,
225 '
.'
=> fstate
= FormatStatePrecision
,
227 flags
.width
= cur
as usize - '
0'
as usize;
228 fstate
= FormatStateWidth
;
232 state
= FormatPattern(flags
, fstate
);
239 Some(Number(0)) => state
= SeekIfElse(0),
240 Some(Number(_
)) => (),
242 return Err("non-number on stack with conditional".to_string())
244 None
=> return Err("stack is empty".to_string()),
247 'e'
=> state
= SeekIfEnd(0),
249 _
=> return Err(format
!("unrecognized format option {}", cur
)),
253 // params are 1-indexed
254 stack
.push(mparams
[match cur
.to_digit(10) {
255 Some(d
) => d
as usize - 1,
256 None
=> return Err("bad param number".to_string()),
261 if cur
>= 'A'
&& cur
<= 'Z'
{
262 if let Some(arg
) = stack
.pop() {
263 let idx
= (cur
as u8) - b'A'
;
264 vars
.sta
[idx
as usize] = arg
;
266 return Err("stack is empty".to_string());
268 } else if cur
>= 'a'
&& cur
<= 'z'
{
269 if let Some(arg
) = stack
.pop() {
270 let idx
= (cur
as u8) - b'a'
;
271 vars
.dyn[idx
as usize] = arg
;
273 return Err("stack is empty".to_string());
276 return Err("bad variable name in %P".to_string());
280 if cur
>= 'A'
&& cur
<= 'Z'
{
281 let idx
= (cur
as u8) - b'A'
;
282 stack
.push(vars
.sta
[idx
as usize].clone());
283 } else if cur
>= 'a'
&& cur
<= 'z'
{
284 let idx
= (cur
as u8) - b'a'
;
285 stack
.push(vars
.dyn[idx
as usize].clone());
287 return Err("bad variable name in %g".to_string());
291 stack
.push(Number(c
as i32));
296 return Err("malformed character constant".to_string());
301 stack
.push(Number(i
));
303 } else if let Some(digit
) = cur
.to_digit(10) {
304 match i
.checked_mul(10).and_then(|i_ten
| i_ten
.checked_add(digit
as i32)) {
306 state
= IntConstant(i
);
309 None
=> return Err("int constant too large".to_string()),
312 return Err("bad int constant".to_string());
315 FormatPattern(ref mut flags
, ref mut fstate
) => {
317 match (*fstate
, cur
) {
318 (_
, 'd'
) | (_
, 'o'
) | (_
, 'x'
) | (_
, 'X'
) | (_
, 's'
) => {
319 if let Some(arg
) = stack
.pop() {
320 let res
= format(arg
, FormatOp
::from_char(cur
), *flags
)?
;
321 output
.extend(res
.iter().map(|x
| *x
));
322 // will cause state to go to Nothing
323 old_state
= FormatPattern(*flags
, *fstate
);
325 return Err("stack is empty".to_string());
328 (FormatStateFlags
, '
#') => {
329 flags
.alternate
= true;
331 (FormatStateFlags
, '
-'
) => {
334 (FormatStateFlags
, '
+'
) => {
337 (FormatStateFlags
, ' '
) => {
340 (FormatStateFlags
, '
0'
...'
9'
) => {
341 flags
.width
= cur
as usize - '
0'
as usize;
342 *fstate
= FormatStateWidth
;
344 (FormatStateFlags
, '
.'
) => {
345 *fstate
= FormatStatePrecision
;
347 (FormatStateWidth
, '
0'
...'
9'
) => {
348 let old
= flags
.width
;
349 flags
.width
= flags
.width
* 10 + (cur
as usize - '
0'
as usize);
350 if flags
.width
< old
{
351 return Err("format width overflow".to_string());
354 (FormatStateWidth
, '
.'
) => {
355 *fstate
= FormatStatePrecision
;
357 (FormatStatePrecision
, '
0'
...'
9'
) => {
358 let old
= flags
.precision
;
359 flags
.precision
= flags
.precision
* 10 + (cur
as usize - '
0'
as usize);
360 if flags
.precision
< old
{
361 return Err("format precision overflow".to_string());
364 _
=> return Err("invalid format specifier".to_string()),
367 SeekIfElse(level
) => {
369 state
= SeekIfElsePercent(level
);
373 SeekIfElsePercent(level
) => {
378 state
= SeekIfElse(level
- 1);
380 } else if cur
== 'e'
&& level
== 0 {
382 } else if cur
== '?'
{
383 state
= SeekIfElse(level
+ 1);
385 state
= SeekIfElse(level
);
388 SeekIfEnd(level
) => {
390 state
= SeekIfEndPercent(level
);
394 SeekIfEndPercent(level
) => {
399 state
= SeekIfEnd(level
- 1);
401 } else if cur
== '?'
{
402 state
= SeekIfEnd(level
+ 1);
404 state
= SeekIfEnd(level
);
408 if state
== old_state
{
415 #[derive(Copy, PartialEq, Clone)]
438 #[derive(Copy, Clone)]
448 fn from_char(c
: char) -> FormatOp
{
455 _
=> panic
!("bad FormatOp char"),
458 fn to_char(self) -> char {
469 fn format(val
: Param
, op
: FormatOp
, flags
: Flags
) -> Result
<Vec
<u8>, String
> {
470 let mut s
= match val
{
475 format
!("{:+01$}", d
, flags
.precision
)
477 // C doesn't take sign into account in precision calculation.
478 format
!("{:01$}", d
, flags
.precision
+ 1)
479 } else if flags
.space
{
480 format
!(" {:01$}", d
, flags
.precision
)
482 format
!("{:01$}", d
, flags
.precision
)
487 // Leading octal zero counts against precision.
488 format
!("0{:01$o}", d
, flags
.precision
.saturating_sub(1))
490 format
!("{:01$o}", d
, flags
.precision
)
494 if flags
.alternate
&& d
!= 0 {
495 format
!("0x{:01$x}", d
, flags
.precision
)
497 format
!("{:01$x}", d
, flags
.precision
)
501 if flags
.alternate
&& d
!= 0 {
502 format
!("0X{:01$X}", d
, flags
.precision
)
504 format
!("{:01$X}", d
, flags
.precision
)
507 FormatString
=> return Err("non-number on stack with %s".to_string()),
514 let mut s
= s
.into_bytes();
515 if flags
.precision
> 0 && flags
.precision
< s
.len() {
516 s
.truncate(flags
.precision
);
520 _
=> return Err(format
!("non-string on stack with %{}", op
.to_char())),
524 if flags
.width
> s
.len() {
525 let n
= flags
.width
- s
.len();
527 s
.extend(repeat(b' '
).take(n
));
529 let mut s_
= Vec
::with_capacity(flags
.width
);
530 s_
.extend(repeat(b' '
).take(n
));
531 s_
.extend(s
.into_iter());
540 use super::{expand, Variables}
;
541 use super::Param
::{self, Words, Number}
;
542 use std
::result
::Result
::Ok
;
545 fn test_basic_setabf() {
546 let s
= b
"\\E[48;5;%p1%dm";
547 assert_eq
!(expand(s
, &[Number(1)], &mut Variables
::new()).unwrap(),
548 "\\E[48;5;1m".bytes().collect
::<Vec
<_
>>());
552 fn test_multiple_int_constants() {
553 assert_eq
!(expand(b
"%{1}%{2}%d%d", &[], &mut Variables
::new()).unwrap(),
554 "21".bytes().collect
::<Vec
<_
>>());
559 let mut vars
= Variables
::new();
560 assert_eq
!(expand(b
"%p1%d%p2%d%p3%d%i%p1%d%p2%d%p3%d",
561 &[Number(1), Number(2), Number(3)],
563 Ok("123233".bytes().collect
::<Vec
<_
>>()));
564 assert_eq
!(expand(b
"%p1%d%p2%d%i%p1%d%p2%d", &[], &mut vars
),
565 Ok("0011".bytes().collect
::<Vec
<_
>>()));
569 fn test_param_stack_failure_conditions() {
570 let mut varstruct
= Variables
::new();
571 let vars
= &mut varstruct
;
572 fn get_res(fmt
: &str,
575 vars
: &mut Variables
)
576 -> Result
<Vec
<u8>, String
> {
577 let mut u8v
: Vec
<_
> = fmt
.bytes().collect();
578 u8v
.extend(cap
.as_bytes().iter().map(|&b
| b
));
579 expand(&u8v
, params
, vars
)
582 let caps
= ["%d", "%c", "%s", "%Pa", "%l", "%!", "%~"];
583 for &cap
in caps
.iter() {
584 let res
= get_res("", cap
, &[], vars
);
585 assert
!(res
.is_err(),
586 "Op {} succeeded incorrectly with 0 stack entries",
588 let p
= if cap
== "%s" || cap
== "%l" {
589 Words("foo".to_string())
593 let res
= get_res("%p1", cap
, &[p
], vars
);
595 "Op {} failed with 1 stack entry: {}",
599 let caps
= ["%+", "%-", "%*", "%/", "%m", "%&", "%|", "%A", "%O"];
600 for &cap
in caps
.iter() {
601 let res
= expand(cap
.as_bytes(), &[], vars
);
602 assert
!(res
.is_err(),
603 "Binop {} succeeded incorrectly with 0 stack entries",
605 let res
= get_res("%{1}", cap
, &[], vars
);
606 assert
!(res
.is_err(),
607 "Binop {} succeeded incorrectly with 1 stack entry",
609 let res
= get_res("%{1}%{2}", cap
, &[], vars
);
611 "Binop {} failed with 2 stack entries: {}",
618 fn test_push_bad_param() {
619 assert
!(expand(b
"%pa", &[], &mut Variables
::new()).is_err());
623 fn test_comparison_ops() {
624 let v
= [('
<'
, [1u8, 0u8, 0u8]), ('
='
, [0u8, 1u8, 0u8]), ('
>'
, [0u8, 0u8, 1u8])];
625 for &(op
, bs
) in v
.iter() {
626 let s
= format
!("%{{1}}%{{2}}%{}%d", op
);
627 let res
= expand(s
.as_bytes(), &[], &mut Variables
::new());
628 assert
!(res
.is_ok(), res
.err().unwrap());
629 assert_eq
!(res
.unwrap(), vec
![b'
0'
+ bs
[0]]);
630 let s
= format
!("%{{1}}%{{1}}%{}%d", op
);
631 let res
= expand(s
.as_bytes(), &[], &mut Variables
::new());
632 assert
!(res
.is_ok(), res
.err().unwrap());
633 assert_eq
!(res
.unwrap(), vec
![b'
0'
+ bs
[1]]);
634 let s
= format
!("%{{2}}%{{1}}%{}%d", op
);
635 let res
= expand(s
.as_bytes(), &[], &mut Variables
::new());
636 assert
!(res
.is_ok(), res
.err().unwrap());
637 assert_eq
!(res
.unwrap(), vec
![b'
0'
+ bs
[2]]);
642 fn test_conditionals() {
643 let mut vars
= Variables
::new();
644 let s
= b
"\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m";
645 let res
= expand(s
, &[Number(1)], &mut vars
);
646 assert
!(res
.is_ok(), res
.err().unwrap());
647 assert_eq
!(res
.unwrap(), "\\E[31m".bytes().collect
::<Vec
<_
>>());
648 let res
= expand(s
, &[Number(8)], &mut vars
);
649 assert
!(res
.is_ok(), res
.err().unwrap());
650 assert_eq
!(res
.unwrap(), "\\E[90m".bytes().collect
::<Vec
<_
>>());
651 let res
= expand(s
, &[Number(42)], &mut vars
);
652 assert
!(res
.is_ok(), res
.err().unwrap());
653 assert_eq
!(res
.unwrap(), "\\E[38;5;42m".bytes().collect
::<Vec
<_
>>());
658 let mut varstruct
= Variables
::new();
659 let vars
= &mut varstruct
;
660 assert_eq
!(expand(b
"%p1%s%p2%2s%p3%2s%p4%.2s",
661 &[Words("foo".to_string()),
662 Words("foo".to_string()),
663 Words("f".to_string()),
664 Words("foo".to_string())],
666 Ok("foofoo ffo".bytes().collect
::<Vec
<_
>>()));
667 assert_eq
!(expand(b
"%p1%:-4.2s", &[Words("foo".to_string())], vars
),
668 Ok("fo ".bytes().collect
::<Vec
<_
>>()));
670 assert_eq
!(expand(b
"%p1%d%p1%.3d%p1%5d%p1%:+d", &[Number(1)], vars
),
671 Ok("1001 1+1".bytes().collect
::<Vec
<_
>>()));
672 assert_eq
!(expand(b
"%p1%o%p1%#o%p2%6.4x%p2%#6.4X",
673 &[Number(15), Number(27)],
675 Ok("17017 001b0X001B".bytes().collect
::<Vec
<_
>>()));