1 // Copyright 2019 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
16 use std
::iter
::repeat
;
18 #[derive(Clone, Copy, PartialEq)]
29 FormatPattern(Flags
, FormatState
),
31 SeekIfElsePercent(usize),
33 SeekIfEndPercent(usize),
36 #[derive(Copy, PartialEq, Clone)]
43 /// Types of parameters a capability can use
44 #[allow(missing_docs)]
51 impl Default
for Param
{
52 fn default() -> Self {
57 /// An error from interpreting a parameterized string.
58 #[derive(Debug, Eq, PartialEq)]
60 /// Data was requested from the stack, but the stack didn't have enough elements.
62 /// The type of the element(s) on top of the stack did not match the type that the operator
65 /// An unrecognized format option was used.
66 UnrecognizedFormatOption(char),
67 /// An invalid variable name was used.
68 InvalidVariableName(char),
69 /// An invalid parameter index was used.
70 InvalidParameterIndex(char),
71 /// A malformed character constant was used.
72 MalformedCharacterConstant
,
73 /// An integer constant was too large (overflowed an i32)
74 IntegerConstantOverflow
,
75 /// A malformed integer constant was used.
76 MalformedIntegerConstant
,
77 /// A format width constant was too large (overflowed a usize)
79 /// A format precision constant was too large (overflowed a usize)
80 FormatPrecisionOverflow
,
83 impl ::std
::fmt
::Display
for Error
{
84 fn fmt(&self, f
: &mut ::std
::fmt
::Formatter
<'_
>) -> ::std
::fmt
::Result
{
85 use std
::error
::Error
;
86 f
.write_str(self.description())
90 impl ::std
::error
::Error
for Error
{
91 fn description(&self) -> &str {
94 StackUnderflow
=> "not enough elements on the stack",
95 TypeMismatch
=> "type mismatch",
96 UnrecognizedFormatOption(_
) => "unrecognized format option",
97 InvalidVariableName(_
) => "invalid variable name",
98 InvalidParameterIndex(_
) => "invalid parameter index",
99 MalformedCharacterConstant
=> "malformed character constant",
100 IntegerConstantOverflow
=> "integer constant computation overflowed",
101 MalformedIntegerConstant
=> "malformed integer constant",
102 FormatWidthOverflow
=> "format width constant computation overflowed",
103 FormatPrecisionOverflow
=> "format precision constant computation overflowed",
107 fn cause(&self) -> Option
<&dyn (::std
::error
::Error
)> {
112 /// Container for static and dynamic variable arrays
114 pub struct Variables
{
115 /// Static variables A-Z
116 sta_vars
: [Param
; 26],
117 /// Dynamic variables a-z
118 dyn_vars
: [Param
; 26],
122 /// Return a new zero-initialized Variables
123 pub fn new() -> Variables
{
128 /// Expand a parameterized capability
131 /// * `cap` - string to expand
132 /// * `params` - vector of params for %p1 etc
133 /// * `vars` - Variables struct for %Pa etc
135 /// To be compatible with ncurses, `vars` should be the same between calls to `expand` for
136 /// multiple capabilities for the same terminal.
137 pub fn expand(cap
: &[u8], params
: &[Param
], vars
: &mut Variables
) -> Result
<Vec
<u8>, Error
> {
138 let mut state
= Nothing
;
140 // expanded cap will only rarely be larger than the cap itself
141 let mut output
= Vec
::with_capacity(cap
.len());
143 let mut stack
: Vec
<Param
> = Vec
::new();
145 // Copy parameters into a local vector for mutability
157 for (dst
, src
) in mparams
.iter_mut().zip(params
.iter()) {
158 *dst
= (*src
).clone();
161 for &c
in cap
.iter() {
163 let mut old_state
= state
;
168 } else if cur
== '$'
{
188 // if c is 0, use 0200 (128) for ncurses compatibility
189 Some(Number(0)) => output
.push(128u8),
190 // Don't check bounds. ncurses just casts and truncates.
191 Some(Number(c
)) => output
.push(c
as u8),
192 Some(_
) => return Err(Error
::TypeMismatch
),
193 None
=> return Err(Error
::StackUnderflow
),
196 'p'
=> state
= PushParam
,
197 'P'
=> state
= SetVar
,
198 'g'
=> state
= GetVar
,
199 '
\''
=> state
= CharConstant
,
200 '
{'
=> state
= IntConstant(0),
201 'l'
=> match stack
.pop() {
202 Some(Words(s
)) => stack
.push(Number(s
.len() as i32)),
203 Some(_
) => return Err(Error
::TypeMismatch
),
204 None
=> return Err(Error
::StackUnderflow
),
206 '
+'
| '
-'
| '
/'
| '
*'
| '
^' | '
&'
| '
|'
| 'm'
=> {
207 match (stack
.pop(), stack
.pop()) {
208 (Some(Number(y
)), Some(Number(x
))) => stack
.push(Number(match cur
{
217 _
=> unreachable
!("logic error"),
219 (Some(_
), Some(_
)) => return Err(Error
::TypeMismatch
),
220 _
=> return Err(Error
::StackUnderflow
),
223 '
='
| '
>'
| '
<'
| 'A'
| 'O'
=> match (stack
.pop(), stack
.pop()) {
224 (Some(Number(y
)), Some(Number(x
))) => stack
.push(Number(
229 'A'
=> x
> 0 && y
> 0,
230 'O'
=> x
> 0 || y
> 0,
231 _
=> unreachable
!("logic error"),
238 (Some(_
), Some(_
)) => return Err(Error
::TypeMismatch
),
239 _
=> return Err(Error
::StackUnderflow
),
241 '
!'
| '
~'
=> match stack
.pop() {
242 Some(Number(x
)) => stack
.push(Number(match cur
{
246 _
=> unreachable
!("logic error"),
248 Some(_
) => return Err(Error
::TypeMismatch
),
249 None
=> return Err(Error
::StackUnderflow
),
251 'i'
=> match (&mparams
[0], &mparams
[1]) {
252 (&Number(x
), &Number(y
)) => {
253 mparams
[0] = Number(x
+ 1);
254 mparams
[1] = Number(y
+ 1);
256 (_
, _
) => return Err(Error
::TypeMismatch
),
259 // printf-style support for %doxXs
260 'd'
| 'o'
| 'x'
| 'X'
| 's'
=> {
261 if let Some(arg
) = stack
.pop() {
262 let flags
= Flags
::default();
263 let res
= format(arg
, FormatOp
::from_char(cur
), flags
)?
;
266 return Err(Error
::StackUnderflow
);
269 '
:'
| '
#' | ' ' | '.' | '0'..='9' => {
270 let mut flags
= Flags
::default();
271 let mut fstate
= FormatState
::Flags
;
274 '
#' => flags.alternate = true,
275 ' '
=> flags
.space
= true,
276 '
.'
=> fstate
= FormatState
::Precision
,
278 flags
.width
= cur
as usize - '
0'
as usize;
279 fstate
= FormatState
::Width
;
281 _
=> unreachable
!("logic error"),
283 state
= FormatPattern(flags
, fstate
);
288 't'
=> match stack
.pop() {
289 Some(Number(0)) => state
= SeekIfElse(0),
290 Some(Number(_
)) => (),
291 Some(_
) => return Err(Error
::TypeMismatch
),
292 None
=> return Err(Error
::StackUnderflow
),
294 'e'
=> state
= SeekIfEnd(0),
295 c
=> return Err(Error
::UnrecognizedFormatOption(c
)),
299 // params are 1-indexed
301 mparams
[match cur
.to_digit(10) {
302 Some(d
) => d
as usize - 1,
303 None
=> return Err(Error
::InvalidParameterIndex(cur
)),
309 if cur
>= 'A'
&& cur
<= 'Z'
{
310 if let Some(arg
) = stack
.pop() {
311 let idx
= (cur
as u8) - b'A'
;
312 vars
.sta_vars
[idx
as usize] = arg
;
314 return Err(Error
::StackUnderflow
);
316 } else if cur
>= 'a'
&& cur
<= 'z'
{
317 if let Some(arg
) = stack
.pop() {
318 let idx
= (cur
as u8) - b'a'
;
319 vars
.dyn_vars
[idx
as usize] = arg
;
321 return Err(Error
::StackUnderflow
);
324 return Err(Error
::InvalidVariableName(cur
));
328 if cur
>= 'A'
&& cur
<= 'Z'
{
329 let idx
= (cur
as u8) - b'A'
;
330 stack
.push(vars
.sta_vars
[idx
as usize].clone());
331 } else if cur
>= 'a'
&& cur
<= 'z'
{
332 let idx
= (cur
as u8) - b'a'
;
333 stack
.push(vars
.dyn_vars
[idx
as usize].clone());
335 return Err(Error
::InvalidVariableName(cur
));
339 stack
.push(Number(i32::from(c
)));
344 return Err(Error
::MalformedCharacterConstant
);
349 stack
.push(Number(i
));
351 } else if let Some(digit
) = cur
.to_digit(10) {
354 .and_then(|i_ten
| i_ten
.checked_add(digit
as i32))
357 state
= IntConstant(i
);
360 None
=> return Err(Error
::IntegerConstantOverflow
),
363 return Err(Error
::MalformedIntegerConstant
);
366 FormatPattern(ref mut flags
, ref mut fstate
) => {
368 match (*fstate
, cur
) {
369 (_
, 'd'
) | (_
, 'o'
) | (_
, 'x'
) | (_
, 'X'
) | (_
, 's'
) => {
370 if let Some(arg
) = stack
.pop() {
371 let res
= format(arg
, FormatOp
::from_char(cur
), *flags
)?
;
373 // will cause state to go to Nothing
374 old_state
= FormatPattern(*flags
, *fstate
);
376 return Err(Error
::StackUnderflow
);
379 (FormatState
::Flags
, '
#') => {
380 flags
.alternate
= true;
382 (FormatState
::Flags
, '
-'
) => {
385 (FormatState
::Flags
, '
+'
) => {
388 (FormatState
::Flags
, ' '
) => {
391 (FormatState
::Flags
, '
0'
..='
9'
) => {
392 flags
.width
= cur
as usize - '
0'
as usize;
393 *fstate
= FormatState
::Width
;
395 (FormatState
::Width
, '
0'
..='
9'
) => {
396 flags
.width
= match flags
399 .and_then(|w
| w
.checked_add(cur
as usize - '
0'
as usize))
401 Some(width
) => width
,
402 None
=> return Err(Error
::FormatWidthOverflow
),
405 (FormatState
::Width
, '
.'
) | (FormatState
::Flags
, '
.'
) => {
406 *fstate
= FormatState
::Precision
;
408 (FormatState
::Precision
, '
0'
..='
9'
) => {
409 flags
.precision
= match flags
412 .and_then(|w
| w
.checked_add(cur
as usize - '
0'
as usize))
414 Some(precision
) => precision
,
415 None
=> return Err(Error
::FormatPrecisionOverflow
),
418 _
=> return Err(Error
::UnrecognizedFormatOption(cur
)),
421 SeekIfElse(level
) => {
423 state
= SeekIfElsePercent(level
);
427 SeekIfElsePercent(level
) => {
432 state
= SeekIfElse(level
- 1);
434 } else if cur
== 'e'
&& level
== 0 {
436 } else if cur
== '?'
{
437 state
= SeekIfElse(level
+ 1);
439 state
= SeekIfElse(level
);
442 SeekIfEnd(level
) => {
444 state
= SeekIfEndPercent(level
);
448 SeekIfEndPercent(level
) => {
453 state
= SeekIfEnd(level
- 1);
455 } else if cur
== '?'
{
456 state
= SeekIfEnd(level
+ 1);
458 state
= SeekIfEnd(level
);
462 if state
== old_state
{
469 #[derive(Copy, PartialEq, Clone, Default)]
479 #[derive(Copy, Clone)]
489 fn from_char(c
: char) -> FormatOp
{
490 use self::FormatOp
::*;
497 _
=> panic
!("bad FormatOp char"),
502 fn format(val
: Param
, op
: FormatOp
, flags
: Flags
) -> Result
<Vec
<u8>, Error
> {
503 use self::FormatOp
::*;
504 let mut s
= match val
{
509 format
!("{:+01$}", d
, flags
.precision
)
511 // C doesn't take sign into account in precision calculation.
512 format
!("{:01$}", d
, flags
.precision
+ 1)
513 } else if flags
.space
{
514 format
!(" {:01$}", d
, flags
.precision
)
516 format
!("{:01$}", d
, flags
.precision
)
521 // Leading octal zero counts against precision.
522 format
!("0{:01$o}", d
, flags
.precision
.saturating_sub(1))
524 format
!("{:01$o}", d
, flags
.precision
)
528 if flags
.alternate
&& d
!= 0 {
529 format
!("0x{:01$x}", d
, flags
.precision
)
531 format
!("{:01$x}", d
, flags
.precision
)
535 if flags
.alternate
&& d
!= 0 {
536 format
!("0X{:01$X}", d
, flags
.precision
)
538 format
!("{:01$X}", d
, flags
.precision
)
541 String
=> return Err(Error
::TypeMismatch
),
545 Words(s
) => match op
{
547 let mut s
= s
.into_bytes();
548 if flags
.precision
> 0 && flags
.precision
< s
.len() {
549 s
.truncate(flags
.precision
);
553 _
=> return Err(Error
::TypeMismatch
),
556 if flags
.width
> s
.len() {
557 let n
= flags
.width
- s
.len();
559 s
.extend(repeat(b' '
).take(n
));
561 let mut s_
= Vec
::with_capacity(flags
.width
);
562 s_
.extend(repeat(b' '
).take(n
));
563 s_
.extend(s
.into_iter());
572 use super::Param
::{self, Number, Words}
;
573 use super::{expand, Variables}
;
574 use std
::result
::Result
::Ok
;
577 fn test_basic_setabf() {
578 let s
= b
"\\E[48;5;%p1%dm";
580 expand(s
, &[Number(1)], &mut Variables
::new()).unwrap(),
581 "\\E[48;5;1m".bytes().collect
::<Vec
<_
>>()
586 fn test_multiple_int_constants() {
588 expand(b
"%{1}%{2}%d%d", &[], &mut Variables
::new()).unwrap(),
589 "21".bytes().collect
::<Vec
<_
>>()
595 let mut vars
= Variables
::new();
598 b
"%p1%d%p2%d%p3%d%i%p1%d%p2%d%p3%d",
599 &[Number(1), Number(2), Number(3)],
602 Ok("123233".bytes().collect
::<Vec
<_
>>())
605 expand(b
"%p1%d%p2%d%i%p1%d%p2%d", &[], &mut vars
),
606 Ok("0011".bytes().collect
::<Vec
<_
>>())
611 fn test_param_stack_failure_conditions() {
612 let mut varstruct
= Variables
::new();
613 let vars
= &mut varstruct
;
618 vars
: &mut Variables
,
619 ) -> Result
<Vec
<u8>, super::Error
> {
620 let mut u8v
: Vec
<_
> = fmt
.bytes().collect();
621 u8v
.extend(cap
.as_bytes().iter().cloned());
622 expand(&u8v
, params
, vars
)
625 let caps
= ["%d", "%c", "%s", "%Pa", "%l", "%!", "%~"];
627 let res
= get_res("", cap
, &[], vars
);
630 "Op {} succeeded incorrectly with 0 stack entries",
633 let p
= if cap
== "%s" || cap
== "%l" {
634 Words("foo".to_owned())
638 let res
= get_res("%p1", cap
, &[p
], vars
);
641 "Op {} failed with 1 stack entry: {}",
646 let caps
= ["%+", "%-", "%*", "%/", "%m", "%&", "%|", "%A", "%O"];
648 let res
= expand(cap
.as_bytes(), &[], vars
);
651 "Binop {} succeeded incorrectly with 0 stack entries",
654 let res
= get_res("%{1}", cap
, &[], vars
);
657 "Binop {} succeeded incorrectly with 1 stack entry",
660 let res
= get_res("%{1}%{2}", cap
, &[], vars
);
663 "Binop {} failed with 2 stack entries: {}",
671 fn test_push_bad_param() {
672 assert
!(expand(b
"%pa", &[], &mut Variables
::new()).is_err());
676 fn test_comparison_ops() {
678 ('
<'
, [1u8, 0u8, 0u8]),
679 ('
='
, [0u8, 1u8, 0u8]),
680 ('
>'
, [0u8, 0u8, 1u8]),
682 for &(op
, bs
) in &v
{
683 let s
= format
!("%{{1}}%{{2}}%{}%d", op
);
684 let res
= expand(s
.as_bytes(), &[], &mut Variables
::new());
685 assert
!(res
.is_ok(), res
.err().unwrap());
686 assert_eq
!(res
.unwrap(), vec
![b'
0'
+ bs
[0]]);
687 let s
= format
!("%{{1}}%{{1}}%{}%d", op
);
688 let res
= expand(s
.as_bytes(), &[], &mut Variables
::new());
689 assert
!(res
.is_ok(), res
.err().unwrap());
690 assert_eq
!(res
.unwrap(), vec
![b'
0'
+ bs
[1]]);
691 let s
= format
!("%{{2}}%{{1}}%{}%d", op
);
692 let res
= expand(s
.as_bytes(), &[], &mut Variables
::new());
693 assert
!(res
.is_ok(), res
.err().unwrap());
694 assert_eq
!(res
.unwrap(), vec
![b'
0'
+ bs
[2]]);
699 fn test_conditionals() {
700 let mut vars
= Variables
::new();
701 let s
= b
"\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m";
702 let res
= expand(s
, &[Number(1)], &mut vars
);
703 assert
!(res
.is_ok(), res
.err().unwrap());
704 assert_eq
!(res
.unwrap(), "\\E[31m".bytes().collect
::<Vec
<_
>>());
705 let res
= expand(s
, &[Number(8)], &mut vars
);
706 assert
!(res
.is_ok(), res
.err().unwrap());
707 assert_eq
!(res
.unwrap(), "\\E[90m".bytes().collect
::<Vec
<_
>>());
708 let res
= expand(s
, &[Number(42)], &mut vars
);
709 assert
!(res
.is_ok(), res
.err().unwrap());
710 assert_eq
!(res
.unwrap(), "\\E[38;5;42m".bytes().collect
::<Vec
<_
>>());
715 let mut varstruct
= Variables
::new();
716 let vars
= &mut varstruct
;
719 b
"%p1%s%p2%2s%p3%2s%p4%.2s",
721 Words("foo".to_owned()),
722 Words("foo".to_owned()),
723 Words("f".to_owned()),
724 Words("foo".to_owned())
728 Ok("foofoo ffo".bytes().collect
::<Vec
<_
>>())
731 expand(b
"%p1%:-4.2s", &[Words("foo".to_owned())], vars
),
732 Ok("fo ".bytes().collect
::<Vec
<_
>>())
736 expand(b
"%p1%d%p1%.3d%p1%5d%p1%:+d", &[Number(1)], vars
),
737 Ok("1001 1+1".bytes().collect
::<Vec
<_
>>())
741 b
"%p1%o%p1%#o%p2%6.4x%p2%#6.4X",
742 &[Number(15), Number(27)],
745 Ok("17017 001b0X001B".bytes().collect
::<Vec
<_
>>())