1be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbarclass Command:
2be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def __init__(self, args, redirects):
3be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.args = list(args)
4be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.redirects = list(redirects)
5be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
6be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def __repr__(self):
7be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        return 'Command(%r, %r)' % (self.args, self.redirects)
8be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
9304a0b03980996669ce2afc09b2bfe21381c5fdfDaniel Dunbar    def __eq__(self, other):
10be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        if not isinstance(other, Command):
11304a0b03980996669ce2afc09b2bfe21381c5fdfDaniel Dunbar            return False
12be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
13304a0b03980996669ce2afc09b2bfe21381c5fdfDaniel Dunbar        return ((self.args, self.redirects) ==
14304a0b03980996669ce2afc09b2bfe21381c5fdfDaniel Dunbar                (other.args, other.redirects))
15be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
16be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def toShell(self, file):
17be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        for arg in self.args:
18be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            if "'" not in arg:
19be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                quoted = "'%s'" % arg
20be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            elif '"' not in arg and '$' not in arg:
21be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                quoted = '"%s"' % arg
22be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            else:
2326e0af54c2eb3efe468715edada97442e469bb19Daniel Dunbar                raise NotImplementedError('Unable to quote %r' % arg)
240d038e3e8852bf4fde949136ca9c2815f64febd0Daniel Dunbar            file.write(quoted)
25be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
26be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            # For debugging / validation.
27ba3931b667ea2c8f30dff4cd176a106d6d4cb51cDaniel Dunbar            import ShUtil
28be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            dequoted = list(ShUtil.ShLexer(quoted).lex())
29be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            if dequoted != [arg]:
3026e0af54c2eb3efe468715edada97442e469bb19Daniel Dunbar                raise NotImplementedError('Unable to quote %r' % arg)
31be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
32be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        for r in self.redirects:
33be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            if len(r[0]) == 1:
340d038e3e8852bf4fde949136ca9c2815f64febd0Daniel Dunbar                file.write("%s '%s'" % (r[0][0], r[1]))
35be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            else:
360d038e3e8852bf4fde949136ca9c2815f64febd0Daniel Dunbar                file.write("%s%s '%s'" % (r[0][1], r[0][0], r[1]))
37be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
38be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbarclass Pipeline:
39be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def __init__(self, commands, negate=False, pipe_err=False):
40be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.commands = commands
41be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.negate = negate
42be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.pipe_err = pipe_err
43be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
44be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def __repr__(self):
45be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        return 'Pipeline(%r, %r, %r)' % (self.commands, self.negate,
46be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar                                         self.pipe_err)
47be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
48304a0b03980996669ce2afc09b2bfe21381c5fdfDaniel Dunbar    def __eq__(self, other):
49be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        if not isinstance(other, Pipeline):
50304a0b03980996669ce2afc09b2bfe21381c5fdfDaniel Dunbar            return False
51be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
52304a0b03980996669ce2afc09b2bfe21381c5fdfDaniel Dunbar        return ((self.commands, self.negate, self.pipe_err) ==
53304a0b03980996669ce2afc09b2bfe21381c5fdfDaniel Dunbar                (other.commands, other.negate, self.pipe_err))
54be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
55be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def toShell(self, file, pipefail=False):
56be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        if pipefail != self.pipe_err:
5726e0af54c2eb3efe468715edada97442e469bb19Daniel Dunbar            raise ValueError('Inconsistent "pipefail" attribute!')
58be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        if self.negate:
590d038e3e8852bf4fde949136ca9c2815f64febd0Daniel Dunbar            file.write('! ')
60be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        for cmd in self.commands:
61be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            cmd.toShell(file)
62be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar            if cmd is not self.commands[-1]:
630d038e3e8852bf4fde949136ca9c2815f64febd0Daniel Dunbar                file.write('|\n  ')
64be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
65be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbarclass Seq:
66be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def __init__(self, lhs, op, rhs):
67be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        assert op in (';', '&', '||', '&&')
68be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.op = op
69be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.lhs = lhs
70be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.rhs = rhs
71be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
72be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def __repr__(self):
73be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        return 'Seq(%r, %r, %r)' % (self.lhs, self.op, self.rhs)
74be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
75304a0b03980996669ce2afc09b2bfe21381c5fdfDaniel Dunbar    def __eq__(self, other):
76be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        if not isinstance(other, Seq):
77304a0b03980996669ce2afc09b2bfe21381c5fdfDaniel Dunbar            return False
78be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
79304a0b03980996669ce2afc09b2bfe21381c5fdfDaniel Dunbar        return ((self.lhs, self.op, self.rhs) ==
80304a0b03980996669ce2afc09b2bfe21381c5fdfDaniel Dunbar                (other.lhs, other.op, other.rhs))
81be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar
82be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar    def toShell(self, file, pipefail=False):
83be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.lhs.toShell(file, pipefail)
840d038e3e8852bf4fde949136ca9c2815f64febd0Daniel Dunbar        file.write(' %s\n' % self.op)
85be7ada718139b8c840a38ba34c4af492b6a05f9fDaniel Dunbar        self.rhs.toShell(file, pipefail)
86