blob: 9d0ce7c36a6aa0a6b3386e399269d9d0a5a92ec6 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import os
16import sys
17
18import pager
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019
Mike Frysinger64477332023-08-21 21:20:32 -040020
Gavin Makea2e3302023-03-11 06:46:20 +000021COLORS = {
22 None: -1,
23 "normal": -1,
24 "black": 0,
25 "red": 1,
26 "green": 2,
27 "yellow": 3,
28 "blue": 4,
29 "magenta": 5,
30 "cyan": 6,
31 "white": 7,
32}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070033
Gavin Makea2e3302023-03-11 06:46:20 +000034ATTRS = {None: -1, "bold": 1, "dim": 2, "ul": 4, "blink": 5, "reverse": 7}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070035
Anthony Kingbdf7ed22015-03-28 21:10:17 +000036RESET = "\033[m"
37
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070038
David Pursehouse8a68ff92012-09-24 12:15:13 +090039def is_color(s):
Gavin Makea2e3302023-03-11 06:46:20 +000040 return s in COLORS
David Pursehouse8a68ff92012-09-24 12:15:13 +090041
Anthony Kingbdf7ed22015-03-28 21:10:17 +000042
David Pursehouse8a68ff92012-09-24 12:15:13 +090043def is_attr(s):
Gavin Makea2e3302023-03-11 06:46:20 +000044 return s in ATTRS
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070045
Anthony Kingbdf7ed22015-03-28 21:10:17 +000046
47def _Color(fg=None, bg=None, attr=None):
Gavin Makea2e3302023-03-11 06:46:20 +000048 fg = COLORS[fg]
49 bg = COLORS[bg]
50 attr = ATTRS[attr]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070051
Gavin Makea2e3302023-03-11 06:46:20 +000052 if attr >= 0 or fg >= 0 or bg >= 0:
53 need_sep = False
54 code = "\033["
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070055
Gavin Makea2e3302023-03-11 06:46:20 +000056 if attr >= 0:
57 code += chr(ord("0") + attr)
58 need_sep = True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070059
Gavin Makea2e3302023-03-11 06:46:20 +000060 if fg >= 0:
61 if need_sep:
62 code += ";"
63 need_sep = True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070064
Gavin Makea2e3302023-03-11 06:46:20 +000065 if fg < 8:
66 code += "3%c" % (ord("0") + fg)
67 else:
68 code += "38;5;%d" % fg
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070069
Gavin Makea2e3302023-03-11 06:46:20 +000070 if bg >= 0:
71 if need_sep:
72 code += ";"
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070073
Gavin Makea2e3302023-03-11 06:46:20 +000074 if bg < 8:
75 code += "4%c" % (ord("0") + bg)
76 else:
77 code += "48;5;%d" % bg
78 code += "m"
79 else:
80 code = ""
81 return code
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070082
David Pursehouse819827a2020-02-12 15:20:19 +090083
Mike Frysinger902665b2014-12-22 15:17:59 -050084DEFAULT = None
85
Anthony Kingbdf7ed22015-03-28 21:10:17 +000086
Mike Frysinger902665b2014-12-22 15:17:59 -050087def SetDefaultColoring(state):
Gavin Makea2e3302023-03-11 06:46:20 +000088 """Set coloring behavior to |state|.
Mike Frysinger902665b2014-12-22 15:17:59 -050089
Gavin Makea2e3302023-03-11 06:46:20 +000090 This is useful for overriding config options via the command line.
91 """
92 if state is None:
93 # Leave it alone -- return quick!
94 return
Mike Frysinger902665b2014-12-22 15:17:59 -050095
Gavin Makea2e3302023-03-11 06:46:20 +000096 global DEFAULT
97 state = state.lower()
98 if state in ("auto",):
99 DEFAULT = state
100 elif state in ("always", "yes", "true", True):
101 DEFAULT = "always"
102 elif state in ("never", "no", "false", False):
103 DEFAULT = "never"
Mike Frysinger902665b2014-12-22 15:17:59 -0500104
105
Mike Frysingerd4aee652023-10-19 05:13:32 -0400106class Coloring:
Gavin Makea2e3302023-03-11 06:46:20 +0000107 def __init__(self, config, section_type):
108 self._section = "color.%s" % section_type
109 self._config = config
110 self._out = sys.stdout
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700111
Gavin Makea2e3302023-03-11 06:46:20 +0000112 on = DEFAULT
113 if on is None:
114 on = self._config.GetString(self._section)
115 if on is None:
116 on = self._config.GetString("color.ui")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700117
Gavin Makea2e3302023-03-11 06:46:20 +0000118 if on == "auto":
119 if pager.active or os.isatty(1):
120 self._on = True
121 else:
122 self._on = False
123 elif on in ("true", "always"):
124 self._on = True
David Pursehouse8a68ff92012-09-24 12:15:13 +0900125 else:
Gavin Makea2e3302023-03-11 06:46:20 +0000126 self._on = False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700127
Gavin Makea2e3302023-03-11 06:46:20 +0000128 def redirect(self, out):
129 self._out = out
130
131 @property
132 def is_on(self):
133 return self._on
134
135 def write(self, fmt, *args):
136 self._out.write(fmt % args)
137
138 def flush(self):
139 self._out.flush()
140
141 def nl(self):
142 self._out.write("\n")
143
144 def printer(self, opt=None, fg=None, bg=None, attr=None):
145 s = self
146 c = self.colorer(opt, fg, bg, attr)
147
148 def f(fmt, *args):
149 s._out.write(c(fmt, *args))
150
151 return f
152
153 def nofmt_printer(self, opt=None, fg=None, bg=None, attr=None):
154 s = self
155 c = self.nofmt_colorer(opt, fg, bg, attr)
156
157 def f(fmt):
158 s._out.write(c(fmt))
159
160 return f
161
162 def colorer(self, opt=None, fg=None, bg=None, attr=None):
163 if self._on:
164 c = self._parse(opt, fg, bg, attr)
165
166 def f(fmt, *args):
167 output = fmt % args
168 return "".join([c, output, RESET])
169
170 return f
171 else:
172
173 def f(fmt, *args):
174 return fmt % args
175
176 return f
177
178 def nofmt_colorer(self, opt=None, fg=None, bg=None, attr=None):
179 if self._on:
180 c = self._parse(opt, fg, bg, attr)
181
182 def f(fmt):
183 return "".join([c, fmt, RESET])
184
185 return f
186 else:
187
188 def f(fmt):
189 return fmt
190
191 return f
192
193 def _parse(self, opt, fg, bg, attr):
194 if not opt:
195 return _Color(fg, bg, attr)
196
Jason R. Coombsb32ccbb2023-09-29 11:04:49 -0400197 v = self._config.GetString(f"{self._section}.{opt}")
Gavin Makea2e3302023-03-11 06:46:20 +0000198 if v is None:
199 return _Color(fg, bg, attr)
200
201 v = v.strip().lower()
202 if v == "reset":
203 return RESET
204 elif v == "":
205 return _Color(fg, bg, attr)
206
207 have_fg = False
208 for a in v.split(" "):
209 if is_color(a):
210 if have_fg:
211 bg = a
212 else:
brightd8b41012024-05-18 07:38:46 +0000213 have_fg = True
Gavin Makea2e3302023-03-11 06:46:20 +0000214 fg = a
215 elif is_attr(a):
216 attr = a
217
218 return _Color(fg, bg, attr)