1use self::Param::*;
14use self::States::*;
15
16use std::iter::repeat;
17
18#[derive(Clone, Copy, PartialEq)]
19enum States {
20 Nothing,
21 Delay,
22 Percent,
23 SetVar,
24 GetVar,
25 PushParam,
26 CharConstant,
27 CharClose,
28 IntConstant(i32),
29 FormatPattern(Flags, FormatState),
30 SeekIfElse(usize),
31 SeekIfElsePercent(usize),
32 SeekIfEnd(usize),
33 SeekIfEndPercent(usize),
34}
35
36#[derive(Copy, PartialEq, Clone)]
37enum FormatState {
38 Flags,
39 Width,
40 Precision,
41}
42
43#[allow(missing_docs)]
45#[derive(Clone)]
46pub enum Param {
47 Number(i32),
48 Words(String),
49}
50
51impl Default for Param {
52 fn default() -> Self {
53 Param::Number(0)
54 }
55}
56
57#[derive(Debug, Eq, PartialEq)]
59pub enum Error {
60 StackUnderflow,
62 TypeMismatch,
65 UnrecognizedFormatOption(char),
67 InvalidVariableName(char),
69 InvalidParameterIndex(char),
71 MalformedCharacterConstant,
73 IntegerConstantOverflow,
75 MalformedIntegerConstant,
77 FormatWidthOverflow,
79 FormatPrecisionOverflow,
81}
82
83impl ::std::fmt::Display for Error {
84 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
85 use self::Error::*;
86 match self {
87 StackUnderflow => f.write_str("not enough elements on the stack"),
88 TypeMismatch => f.write_str("type mismatch"),
89 UnrecognizedFormatOption(_) => f.write_str("unrecognized format option"),
90 InvalidVariableName(_) => f.write_str("invalid variable name"),
91 InvalidParameterIndex(_) => f.write_str("invalid parameter index"),
92 MalformedCharacterConstant => f.write_str("malformed character constant"),
93 IntegerConstantOverflow => f.write_str("integer constant computation overflowed"),
94 MalformedIntegerConstant => f.write_str("malformed integer constant"),
95 FormatWidthOverflow => f.write_str("format width constant computation overflowed"),
96 FormatPrecisionOverflow => {
97 f.write_str("format precision constant computation overflowed")
98 }
99 }
100 }
101}
102
103impl ::std::error::Error for Error {}
104
105#[derive(Default)]
107pub struct Variables {
108 sta_vars: [Param; 26],
110 dyn_vars: [Param; 26],
112}
113
114impl Variables {
115 pub fn new() -> Variables {
117 Default::default()
118 }
119}
120
121pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables) -> Result<Vec<u8>, Error> {
131 let mut state = Nothing;
132
133 let mut output = Vec::with_capacity(cap.len());
135
136 let mut stack: Vec<Param> = Vec::new();
137
138 let mut mparams = [
140 Number(0),
141 Number(0),
142 Number(0),
143 Number(0),
144 Number(0),
145 Number(0),
146 Number(0),
147 Number(0),
148 Number(0),
149 ];
150 for (dst, src) in mparams.iter_mut().zip(params.iter()) {
151 *dst = (*src).clone();
152 }
153
154 for &c in cap.iter() {
155 let cur = c as char;
156 let mut old_state = state;
157 match state {
158 Nothing => {
159 if cur == '%' {
160 state = Percent;
161 } else if cur == '$' {
162 state = Delay;
163 } else {
164 output.push(c);
165 }
166 }
167 Delay => {
168 old_state = Nothing;
169 if cur == '>' {
170 state = Nothing;
171 }
172 }
173 Percent => {
174 match cur {
175 '%' => {
176 output.push(c);
177 state = Nothing
178 }
179 'c' => {
180 match stack.pop() {
181 Some(Number(0)) => output.push(128u8),
183 Some(Number(c)) => output.push(c as u8),
185 Some(_) => return Err(Error::TypeMismatch),
186 None => return Err(Error::StackUnderflow),
187 }
188 }
189 'p' => state = PushParam,
190 'P' => state = SetVar,
191 'g' => state = GetVar,
192 '\'' => state = CharConstant,
193 '{' => state = IntConstant(0),
194 'l' => match stack.pop() {
195 Some(Words(s)) => stack.push(Number(s.len() as i32)),
196 Some(_) => return Err(Error::TypeMismatch),
197 None => return Err(Error::StackUnderflow),
198 },
199 '+' | '-' | '/' | '*' | '^' | '&' | '|' | 'm' => {
200 match (stack.pop(), stack.pop()) {
201 (Some(Number(y)), Some(Number(x))) => stack.push(Number(match cur {
202 '+' => x + y,
203 '-' => x - y,
204 '*' => x * y,
205 '/' => x / y,
206 '|' => x | y,
207 '&' => x & y,
208 '^' => x ^ y,
209 'm' => x % y,
210 _ => unreachable!("logic error"),
211 })),
212 (Some(_), Some(_)) => return Err(Error::TypeMismatch),
213 _ => return Err(Error::StackUnderflow),
214 }
215 }
216 '=' | '>' | '<' | 'A' | 'O' => match (stack.pop(), stack.pop()) {
217 (Some(Number(y)), Some(Number(x))) => stack.push(Number(
218 if match cur {
219 '=' => x == y,
220 '<' => x < y,
221 '>' => x > y,
222 'A' => x > 0 && y > 0,
223 'O' => x > 0 || y > 0,
224 _ => unreachable!("logic error"),
225 } {
226 1
227 } else {
228 0
229 },
230 )),
231 (Some(_), Some(_)) => return Err(Error::TypeMismatch),
232 _ => return Err(Error::StackUnderflow),
233 },
234 '!' | '~' => match stack.pop() {
235 Some(Number(x)) => stack.push(Number(match cur {
236 '!' if x > 0 => 0,
237 '!' => 1,
238 '~' => !x,
239 _ => unreachable!("logic error"),
240 })),
241 Some(_) => return Err(Error::TypeMismatch),
242 None => return Err(Error::StackUnderflow),
243 },
244 'i' => match (&mparams[0], &mparams[1]) {
245 (&Number(x), &Number(y)) => {
246 mparams[0] = Number(x + 1);
247 mparams[1] = Number(y + 1);
248 }
249 (_, _) => return Err(Error::TypeMismatch),
250 },
251
252 'd' | 'o' | 'x' | 'X' | 's' => {
254 if let Some(arg) = stack.pop() {
255 let flags = Flags::default();
256 let res = format(arg, FormatOp::from_char(cur), flags)?;
257 output.extend(res);
258 } else {
259 return Err(Error::StackUnderflow);
260 }
261 }
262 ':' | '#' | ' ' | '.' | '0'..='9' => {
263 let mut flags = Flags::default();
264 let mut fstate = FormatState::Flags;
265 match cur {
266 ':' => (),
267 '#' => flags.alternate = true,
268 ' ' => flags.space = true,
269 '.' => fstate = FormatState::Precision,
270 '0'..='9' => {
271 flags.width = cur as usize - '0' as usize;
272 fstate = FormatState::Width;
273 }
274 _ => unreachable!("logic error"),
275 }
276 state = FormatPattern(flags, fstate);
277 }
278
279 '?' | ';' => (),
281 't' => match stack.pop() {
282 Some(Number(0)) => state = SeekIfElse(0),
283 Some(Number(_)) => (),
284 Some(_) => return Err(Error::TypeMismatch),
285 None => return Err(Error::StackUnderflow),
286 },
287 'e' => state = SeekIfEnd(0),
288 c => return Err(Error::UnrecognizedFormatOption(c)),
289 }
290 }
291 PushParam => {
292 stack.push(
294 mparams[match cur.to_digit(10) {
295 Some(d) => d as usize - 1,
296 None => return Err(Error::InvalidParameterIndex(cur)),
297 }]
298 .clone(),
299 );
300 }
301 SetVar => match cur {
302 'A'..='Z' => {
303 if let Some(arg) = stack.pop() {
304 let idx = (cur as u8) - b'A';
305 vars.sta_vars[idx as usize] = arg;
306 } else {
307 return Err(Error::StackUnderflow);
308 }
309 }
310 'a'..='z' => {
311 if let Some(arg) = stack.pop() {
312 let idx = (cur as u8) - b'a';
313 vars.dyn_vars[idx as usize] = arg;
314 } else {
315 return Err(Error::StackUnderflow);
316 }
317 }
318 _ => {
319 return Err(Error::InvalidVariableName(cur));
320 }
321 },
322 GetVar => match cur {
323 'A'..='Z' => {
324 let idx = (cur as u8) - b'A';
325 stack.push(vars.sta_vars[idx as usize].clone());
326 }
327 'a'..='z' => {
328 let idx = (cur as u8) - b'a';
329 stack.push(vars.dyn_vars[idx as usize].clone());
330 }
331 _ => {
332 return Err(Error::InvalidVariableName(cur));
333 }
334 },
335 CharConstant => {
336 stack.push(Number(i32::from(c)));
337 state = CharClose;
338 }
339 CharClose => {
340 if cur != '\'' {
341 return Err(Error::MalformedCharacterConstant);
342 }
343 }
344 IntConstant(i) => {
345 if cur == '}' {
346 stack.push(Number(i));
347 state = Nothing;
348 } else if let Some(digit) = cur.to_digit(10) {
349 match i
350 .checked_mul(10)
351 .and_then(|i_ten| i_ten.checked_add(digit as i32))
352 {
353 Some(i) => {
354 state = IntConstant(i);
355 old_state = Nothing;
356 }
357 None => return Err(Error::IntegerConstantOverflow),
358 }
359 } else {
360 return Err(Error::MalformedIntegerConstant);
361 }
362 }
363 FormatPattern(ref mut flags, ref mut fstate) => {
364 old_state = Nothing;
365 match (*fstate, cur) {
366 (_, 'd') | (_, 'o') | (_, 'x') | (_, 'X') | (_, 's') => {
367 if let Some(arg) = stack.pop() {
368 let res = format(arg, FormatOp::from_char(cur), *flags)?;
369 output.extend(res);
370 old_state = FormatPattern(*flags, *fstate);
372 } else {
373 return Err(Error::StackUnderflow);
374 }
375 }
376 (FormatState::Flags, '#') => {
377 flags.alternate = true;
378 }
379 (FormatState::Flags, '-') => {
380 flags.left = true;
381 }
382 (FormatState::Flags, '+') => {
383 flags.sign = true;
384 }
385 (FormatState::Flags, ' ') => {
386 flags.space = true;
387 }
388 (FormatState::Flags, '0'..='9') => {
389 flags.width = cur as usize - '0' as usize;
390 *fstate = FormatState::Width;
391 }
392 (FormatState::Width, '0'..='9') => {
393 flags.width = match flags
394 .width
395 .checked_mul(10)
396 .and_then(|w| w.checked_add(cur as usize - '0' as usize))
397 {
398 Some(width) => width,
399 None => return Err(Error::FormatWidthOverflow),
400 }
401 }
402 (FormatState::Width, '.') | (FormatState::Flags, '.') => {
403 *fstate = FormatState::Precision;
404 }
405 (FormatState::Precision, '0'..='9') => {
406 flags.precision = match flags
407 .precision
408 .checked_mul(10)
409 .and_then(|w| w.checked_add(cur as usize - '0' as usize))
410 {
411 Some(precision) => precision,
412 None => return Err(Error::FormatPrecisionOverflow),
413 }
414 }
415 _ => return Err(Error::UnrecognizedFormatOption(cur)),
416 }
417 }
418 SeekIfElse(level) => {
419 if cur == '%' {
420 state = SeekIfElsePercent(level);
421 }
422 old_state = Nothing;
423 }
424 SeekIfElsePercent(level) => {
425 if cur == ';' {
426 if level == 0 {
427 state = Nothing;
428 } else {
429 state = SeekIfElse(level - 1);
430 }
431 } else if cur == 'e' && level == 0 {
432 state = Nothing;
433 } else if cur == '?' {
434 state = SeekIfElse(level + 1);
435 } else {
436 state = SeekIfElse(level);
437 }
438 }
439 SeekIfEnd(level) => {
440 if cur == '%' {
441 state = SeekIfEndPercent(level);
442 }
443 old_state = Nothing;
444 }
445 SeekIfEndPercent(level) => {
446 if cur == ';' {
447 if level == 0 {
448 state = Nothing;
449 } else {
450 state = SeekIfEnd(level - 1);
451 }
452 } else if cur == '?' {
453 state = SeekIfEnd(level + 1);
454 } else {
455 state = SeekIfEnd(level);
456 }
457 }
458 }
459 if state == old_state {
460 state = Nothing;
461 }
462 }
463 Ok(output)
464}
465
466#[derive(Copy, PartialEq, Clone, Default)]
467struct Flags {
468 width: usize,
469 precision: usize,
470 alternate: bool,
471 left: bool,
472 sign: bool,
473 space: bool,
474}
475
476#[derive(Copy, Clone)]
477enum FormatOp {
478 Digit,
479 Octal,
480 Hex,
481 #[allow(clippy::upper_case_acronyms)]
482 HEX,
483 String,
484}
485
486impl FormatOp {
487 fn from_char(c: char) -> FormatOp {
488 use self::FormatOp::*;
489 match c {
490 'd' => Digit,
491 'o' => Octal,
492 'x' => Hex,
493 'X' => HEX,
494 's' => String,
495 _ => panic!("bad FormatOp char"),
496 }
497 }
498}
499
500fn format(val: Param, op: FormatOp, flags: Flags) -> Result<Vec<u8>, Error> {
501 use self::FormatOp::*;
502 let mut s = match val {
503 Number(d) => {
504 match op {
505 Digit => {
506 if flags.sign {
508 format!("{:+01$}", d, flags.precision + 1)
509 } else if d < 0 {
510 format!("{:01$}", d, flags.precision + 1)
511 } else if flags.space {
512 format!(" {:01$}", d, flags.precision)
513 } else {
514 format!("{:01$}", d, flags.precision)
515 }
516 }
517 Octal => {
518 if flags.alternate {
519 format!("0{:01$o}", d, flags.precision.saturating_sub(1))
521 } else {
522 format!("{:01$o}", d, flags.precision)
523 }
524 }
525 Hex => {
526 if flags.alternate && d != 0 {
527 format!("0x{:01$x}", d, flags.precision)
528 } else {
529 format!("{:01$x}", d, flags.precision)
530 }
531 }
532 HEX => {
533 if flags.alternate && d != 0 {
534 format!("0X{:01$X}", d, flags.precision)
535 } else {
536 format!("{:01$X}", d, flags.precision)
537 }
538 }
539 String => return Err(Error::TypeMismatch),
540 }
541 .into_bytes()
542 }
543 Words(s) => match op {
544 String => {
545 let mut s = s.into_bytes();
546 if flags.precision > 0 && flags.precision < s.len() {
547 s.truncate(flags.precision);
548 }
549 s
550 }
551 _ => return Err(Error::TypeMismatch),
552 },
553 };
554 if flags.width > s.len() {
555 let n = flags.width - s.len();
556 if flags.left {
557 s.extend(repeat(b' ').take(n));
558 } else {
559 let mut s_ = Vec::with_capacity(flags.width);
560 s_.extend(repeat(b' ').take(n));
561 s_.extend(s);
562 s = s_;
563 }
564 }
565 Ok(s)
566}
567
568#[cfg(test)]
569mod test {
570 use super::Param::{self, Number, Words};
571 use super::{expand, Variables};
572 use std::result::Result::Ok;
573
574 #[test]
575 fn test_basic_setabf() {
576 let s = b"\\E[48;5;%p1%dm";
577 assert_eq!(
578 expand(s, &[Number(1)], &mut Variables::new()).unwrap(),
579 "\\E[48;5;1m".bytes().collect::<Vec<_>>()
580 );
581 }
582
583 #[test]
584 fn test_multiple_int_constants() {
585 assert_eq!(
586 expand(b"%{1}%{2}%d%d", &[], &mut Variables::new()).unwrap(),
587 "21".bytes().collect::<Vec<_>>()
588 );
589 }
590
591 #[test]
592 fn test_op_i() {
593 let mut vars = Variables::new();
594 assert_eq!(
595 expand(
596 b"%p1%d%p2%d%p3%d%i%p1%d%p2%d%p3%d",
597 &[Number(1), Number(2), Number(3)],
598 &mut vars
599 ),
600 Ok("123233".bytes().collect::<Vec<_>>())
601 );
602 assert_eq!(
603 expand(b"%p1%d%p2%d%i%p1%d%p2%d", &[], &mut vars),
604 Ok("0011".bytes().collect::<Vec<_>>())
605 );
606 }
607
608 #[test]
609 fn test_param_stack_failure_conditions() {
610 let mut varstruct = Variables::new();
611 let vars = &mut varstruct;
612 fn get_res(
613 fmt: &str,
614 cap: &str,
615 params: &[Param],
616 vars: &mut Variables,
617 ) -> Result<Vec<u8>, super::Error> {
618 let mut u8v: Vec<_> = fmt.bytes().collect();
619 u8v.extend(cap.as_bytes().iter().cloned());
620 expand(&u8v, params, vars)
621 }
622
623 let caps = ["%d", "%c", "%s", "%Pa", "%l", "%!", "%~"];
624 for &cap in &caps {
625 let res = get_res("", cap, &[], vars);
626 assert!(
627 res.is_err(),
628 "Op {} succeeded incorrectly with 0 stack entries",
629 cap
630 );
631 let p = if cap == "%s" || cap == "%l" {
632 Words("foo".to_owned())
633 } else {
634 Number(97)
635 };
636 let res = get_res("%p1", cap, &[p], vars);
637 assert!(
638 res.is_ok(),
639 "Op {} failed with 1 stack entry: {}",
640 cap,
641 res.err().unwrap()
642 );
643 }
644 let caps = ["%+", "%-", "%*", "%/", "%m", "%&", "%|", "%A", "%O"];
645 for &cap in &caps {
646 let res = expand(cap.as_bytes(), &[], vars);
647 assert!(
648 res.is_err(),
649 "Binop {} succeeded incorrectly with 0 stack entries",
650 cap
651 );
652 let res = get_res("%{1}", cap, &[], vars);
653 assert!(
654 res.is_err(),
655 "Binop {} succeeded incorrectly with 1 stack entry",
656 cap
657 );
658 let res = get_res("%{1}%{2}", cap, &[], vars);
659 assert!(
660 res.is_ok(),
661 "Binop {} failed with 2 stack entries: {}",
662 cap,
663 res.err().unwrap()
664 );
665 }
666 }
667
668 #[test]
669 fn test_push_bad_param() {
670 assert!(expand(b"%pa", &[], &mut Variables::new()).is_err());
671 }
672
673 #[test]
674 fn test_comparison_ops() {
675 let v = [
676 ('<', [1u8, 0u8, 0u8]),
677 ('=', [0u8, 1u8, 0u8]),
678 ('>', [0u8, 0u8, 1u8]),
679 ];
680 for &(op, bs) in &v {
681 let s = format!("%{{1}}%{{2}}%{}%d", op);
682 let res = expand(s.as_bytes(), &[], &mut Variables::new());
683 assert!(res.is_ok(), "{}", res.err().unwrap());
684 assert_eq!(res.unwrap(), vec![b'0' + bs[0]]);
685 let s = format!("%{{1}}%{{1}}%{}%d", op);
686 let res = expand(s.as_bytes(), &[], &mut Variables::new());
687 assert!(res.is_ok(), "{}", res.err().unwrap());
688 assert_eq!(res.unwrap(), vec![b'0' + bs[1]]);
689 let s = format!("%{{2}}%{{1}}%{}%d", op);
690 let res = expand(s.as_bytes(), &[], &mut Variables::new());
691 assert!(res.is_ok(), "{}", res.err().unwrap());
692 assert_eq!(res.unwrap(), vec![b'0' + bs[2]]);
693 }
694 }
695
696 #[test]
697 fn test_conditionals() {
698 let mut vars = Variables::new();
699 let s = b"\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m";
700 let res = expand(s, &[Number(1)], &mut vars);
701 assert!(res.is_ok(), "{}", res.err().unwrap());
702 assert_eq!(res.unwrap(), "\\E[31m".bytes().collect::<Vec<_>>());
703 let res = expand(s, &[Number(8)], &mut vars);
704 assert!(res.is_ok(), "{}", res.err().unwrap());
705 assert_eq!(res.unwrap(), "\\E[90m".bytes().collect::<Vec<_>>());
706 let res = expand(s, &[Number(42)], &mut vars);
707 assert!(res.is_ok(), "{}", res.err().unwrap());
708 assert_eq!(res.unwrap(), "\\E[38;5;42m".bytes().collect::<Vec<_>>());
709 }
710
711 #[test]
712 fn test_format() {
713 let mut varstruct = Variables::new();
714 let vars = &mut varstruct;
715 assert_eq!(
716 expand(
717 b"%p1%s%p2%2s%p3%2s%p4%.2s",
718 &[
719 Words("foo".to_owned()),
720 Words("foo".to_owned()),
721 Words("f".to_owned()),
722 Words("foo".to_owned())
723 ],
724 vars
725 ),
726 Ok("foofoo ffo".bytes().collect::<Vec<_>>())
727 );
728 assert_eq!(
729 expand(b"%p1%:-4.2s", &[Words("foo".to_owned())], vars),
730 Ok("fo ".bytes().collect::<Vec<_>>())
731 );
732
733 assert_eq!(
734 expand(b"%p1%d%p1%.3d%p1%5d%p1%:+d", &[Number(1)], vars),
735 Ok("1001 1+1".bytes().collect::<Vec<_>>())
736 );
737 assert_eq!(
738 expand(
739 b"%p1%o%p1%#o%p2%6.4x%p2%#6.4X",
740 &[Number(15), Number(27)],
741 vars
742 ),
743 Ok("17017 001b0X001B".bytes().collect::<Vec<_>>())
744 );
745 assert_eq!(
746 expand(
747 b"%p1%.5d%p1% .5d%p1%:+.5d%p2%.5d%p2% .5d%p2%:+.5d",
748 &[Number(15), Number(-15)],
749 vars
750 ),
751 Ok("00015 00015+00015-00015-00015-00015"
752 .bytes()
753 .collect::<Vec<_>>())
754 );
755 }
756}