1#!/usr/bin/env python
2# Copyright 2016 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import re
7import sys
8
9class CSSMinimizer(object):
10
11  INITIAL = 0
12  MAYBE_COMMENT_START = 1
13  INSIDE_COMMENT = 2
14  MAYBE_COMMENT_END = 3
15  INSIDE_SINGLE_QUOTE = 4
16  INSIDE_SINGLE_QUOTE_ESCAPE = 5
17  INSIDE_DOUBLE_QUOTE = 6
18  INSIDE_DOUBLE_QUOTE_ESCAPE = 7
19
20  def __init__(self):
21    self._output = ''
22    self._codeblock = ''
23
24  def flush_codeblock(self):
25    stripped = re.sub(r"\s+", ' ', self._codeblock)
26    stripped = re.sub(r";?\s*(?P<op>[{};])\s*", r'\g<op>', stripped)
27    self._output += stripped
28    self._codeblock = ''
29
30  def parse(self, content):
31    state = self.INITIAL
32    for char in content:
33      if state == self.INITIAL:
34        if char == '/':
35          state = self.MAYBE_COMMENT_START
36        elif char == "'":
37          self.flush_codeblock()
38          self._output += char
39          state = self.INSIDE_SINGLE_QUOTE
40        elif char == '"':
41          self.flush_codeblock()
42          self._output += char
43          state = self.INSIDE_DOUBLE_QUOTE
44        else:
45          self._codeblock += char
46      elif state == self.MAYBE_COMMENT_START:
47        if char == '*':
48          self.flush_codeblock()
49          state = self.INSIDE_COMMENT
50        else:
51          self._codeblock += '/' + char
52          state = self.INITIAL
53      elif state == self.INSIDE_COMMENT:
54        if char == '*':
55          state = self.MAYBE_COMMENT_END
56        else:
57          pass
58      elif state == self.MAYBE_COMMENT_END:
59        if char == '/':
60          state = self.INITIAL
61        else:
62          state = self.INSIDE_COMMENT
63      elif state == self.INSIDE_SINGLE_QUOTE:
64        if char == '\\':
65          self._output += char
66          state = self.INSIDE_SINGLE_QUOTE_ESCAPE
67        elif char == "'":
68          self._output += char
69          state = self.INITIAL
70        else:
71          self._output += char
72      elif state == self.INSIDE_SINGLE_QUOTE_ESCAPE:
73        self._output += char
74        state = self.INSIDE_SINGLE_QUOTE
75      elif state == self.INSIDE_DOUBLE_QUOTE:
76        if char == '\\':
77          self._output += char
78          state = self.INSIDE_DOUBLE_QUOTE_ESCAPE
79        elif char == '"':
80          self._output += char
81          state = self.INITIAL
82        else:
83          self._output += char
84      elif state == self.INSIDE_DOUBLE_QUOTE_ESCAPE:
85        self._output += char
86        state = self.INSIDE_DOUBLE_QUOTE
87
88    self.flush_codeblock()
89    self._output = self._output.strip()
90    return self._output
91
92  @classmethod
93  def minimize_css(cls, content):
94    minimizer = CSSMinimizer()
95    return minimizer.parse(content)
96
97def main():
98  result = ''
99  try:
100    result = CSSMinimizer.minimize_css(sys.stdin.read())
101  finally:
102    print(result)
103
104if __name__ == '__main__':
105  main()
106