# Copyright (C) 2008 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os import sys import pager COLORS = { None: -1, "normal": -1, "black": 0, "red": 1, "green": 2, "yellow": 3, "blue": 4, "magenta": 5, "cyan": 6, "white": 7, } ATTRS = {None: -1, "bold": 1, "dim": 2, "ul": 4, "blink": 5, "reverse": 7} RESET = "\033[m" def is_color(s): return s in COLORS def is_attr(s): return s in ATTRS def _Color(fg=None, bg=None, attr=None): fg = COLORS[fg] bg = COLORS[bg] attr = ATTRS[attr] if attr >= 0 or fg >= 0 or bg >= 0: need_sep = False code = "\033[" if attr >= 0: code += chr(ord("0") + attr) need_sep = True if fg >= 0: if need_sep: code += ";" need_sep = True if fg < 8: code += "3%c" % (ord("0") + fg) else: code += "38;5;%d" % fg if bg >= 0: if need_sep: code += ";" if bg < 8: code += "4%c" % (ord("0") + bg) else: code += "48;5;%d" % bg code += "m" else: code = "" return code DEFAULT = None def SetDefaultColoring(state): """Set coloring behavior to |state|. This is useful for overriding config options via the command line. """ if state is None: # Leave it alone -- return quick! return global DEFAULT state = state.lower() if state in ("auto",): DEFAULT = state elif state in ("always", "yes", "true", True): DEFAULT = "always" elif state in ("never", "no", "false", False): DEFAULT = "never" class Coloring: def __init__(self, config, section_type): self._section = "color.%s" % section_type self._config = config self._out = sys.stdout on = DEFAULT if on is None: on = self._config.GetString(self._section) if on is None: on = self._config.GetString("color.ui") if on == "auto": if pager.active or os.isatty(1): self._on = True else: self._on = False elif on in ("true", "always"): self._on = True else: self._on = False def redirect(self, out): self._out = out @property def is_on(self): return self._on def write(self, fmt, *args): self._out.write(fmt % args) def flush(self): self._out.flush() def nl(self): self._out.write("\n") def printer(self, opt=None, fg=None, bg=None, attr=None): s = self c = self.colorer(opt, fg, bg, attr) def f(fmt, *args): s._out.write(c(fmt, *args)) return f def nofmt_printer(self, opt=None, fg=None, bg=None, attr=None): s = self c = self.nofmt_colorer(opt, fg, bg, attr) def f(fmt): s._out.write(c(fmt)) return f def colorer(self, opt=None, fg=None, bg=None, attr=None): if self._on: c = self._parse(opt, fg, bg, attr) def f(fmt, *args): output = fmt % args return "".join([c, output, RESET]) return f else: def f(fmt, *args): return fmt % args return f def nofmt_colorer(self, opt=None, fg=None, bg=None, attr=None): if self._on: c = self._parse(opt, fg, bg, attr) def f(fmt): return "".join([c, fmt, RESET]) return f else: def f(fmt): return fmt return f def _parse(self, opt, fg, bg, attr): if not opt: return _Color(fg, bg, attr) v = self._config.GetString(f"{self._section}.{opt}") if v is None: return _Color(fg, bg, attr) v = v.strip().lower() if v == "reset": return RESET elif v == "": return _Color(fg, bg, attr) have_fg = False for a in v.split(" "): if is_color(a): if have_fg: bg = a else: have_fg = True fg = a elif is_attr(a): attr = a return _Color(fg, bg, attr)