1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use env::{StringWrapper, VariableEnvironment};
use future::{Async, EnvFuture, Poll};
use eval::{Fields, TildeExpansion, WordEval, WordEvalConfig};
use std::borrow::Borrow;
use std::iter::Fuse;
use std::mem;

/// Evaluates a list of words as if they were double quoted.
///
/// All words retain any special meaning/behavior they may have, except
/// no tilde expansions will be made, and no fields will be split further.
pub fn double_quoted<W, I, E: ?Sized>(words: I)
    -> DoubleQuoted<W::EvalResult, W, W::EvalFuture, I::IntoIter>
    where W: WordEval<E>,
          I: IntoIterator<Item = W>,
{
    DoubleQuoted {
        fields: Vec::new(),
        cur_field: None,
        future: None,
        rest: words.into_iter().fuse(),
    }
}

/// A future representing the evaluation of a double quoted list of words.
#[must_use = "futures do nothing unless polled"]
#[derive(Debug)]
pub struct DoubleQuoted<T, W, F, I> where I: Iterator<Item = W> {
    fields: Vec<T>,
    cur_field: Option<String>,
    future: Option<F>,
    rest: Fuse<I>,
}

impl<T, W, F, I> DoubleQuoted<T, W, F, I> where I: Iterator<Item = W> {
    fn append_to_cur_field(&mut self, t: T) where T: StringWrapper {
        match self.cur_field {
            Some(ref mut cur) => cur.push_str(t.as_str()),
            None => self.cur_field = Some(t.into_owned()),
        }
    }
}

impl<T, W, I, E: ?Sized> EnvFuture<E> for DoubleQuoted<T, W, W::EvalFuture, I>
    where T: StringWrapper,
          W: WordEval<E, EvalResult = T>,
          I: Iterator<Item = W>,
          E: VariableEnvironment<Var = T>,
          E::VarName: Borrow<String>,
{
    type Item = Fields<T>;
    type Error = W::Error;

    fn poll(&mut self, env: &mut E) -> Poll<Self::Item, Self::Error> {
        loop {
            if self.future.is_none() {
                if let Some(w) = self.rest.next() {
                    // Make sure we are NOT doing any tilde expanions for further field splitting
                    let cfg = WordEvalConfig {
                        tilde_expansion: TildeExpansion::None,
                        split_fields_further: false,
                    };

                    self.future = Some(w.eval_with_config(env, cfg));
                }
            }

            let next = match self.future {
                None => {
                    self.cur_field.take().map(|s| self.fields.push(s.into()));
                    let fields = mem::replace(&mut self.fields, Vec::new());
                    return Ok(Async::Ready(fields.into()));
                }

                Some(ref mut f) => try_ready!(f.poll(env)),
            };

            // Ensure we don't poll twice
            self.future = None;

            match next {
                Fields::Zero => continue,
                Fields::Single(s) => self.append_to_cur_field(s),

                // Since we should have indicated we do NOT want field splitting,
                // we should never encounter a Split variant, however, since we
                // cannot control external implementations, we'll fallback
                // somewhat gracefully rather than panicking.
                f@Fields::Split(_) |
                f@Fields::Star(_) => self.append_to_cur_field(f.join_with_ifs(env)),

                // Any fields generated by $@ must be maintained, however, the first and last
                // fields of $@ should be concatenated to whatever comes before/after them.
                Fields::At(v) => {
                    // According to the POSIX spec, if $@ is empty it should generate NO fields
                    // even when within double quotes.
                    if !v.is_empty() {
                        let mut iter = v.into_iter().fuse();
                        iter.next().map(|s| self.append_to_cur_field(s));

                        self.cur_field.take().map(|s| self.fields.push(s.into()));

                        let mut last = None;
                        for next in iter {
                            self.fields.extend(last.take());
                            last = Some(next);
                        }

                        last.map(|s| self.append_to_cur_field(s));
                    }
                },
            }
        }
    }

    fn cancel(&mut self, env: &mut E) {
        self.future.as_mut().map(|f| f.cancel(env));
    }
}