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