1# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
2#
3# This file is part of Ansible
4#
5# Ansible is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# Ansible is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
17from __future__ import (absolute_import, division, print_function)
18__metaclass__ = type
19
20import re
21import sys
22
23from ansible import constants as C
24
25ANSIBLE_COLOR = True
26if C.ANSIBLE_NOCOLOR:
27    ANSIBLE_COLOR = False
28elif not hasattr(sys.stdout, 'isatty') or not sys.stdout.isatty():
29    ANSIBLE_COLOR = False
30else:
31    try:
32        import curses
33        curses.setupterm()
34        if curses.tigetnum('colors') < 0:
35            ANSIBLE_COLOR = False
36    except ImportError:
37        # curses library was not found
38        pass
39    except curses.error:
40        # curses returns an error (e.g. could not find terminal)
41        ANSIBLE_COLOR = False
42
43if C.ANSIBLE_FORCE_COLOR:
44    ANSIBLE_COLOR = True
45
46# --- begin "pretty"
47#
48# pretty - A miniature library that provides a Python print and stdout
49# wrapper that makes colored terminal text easier to use (e.g. without
50# having to mess around with ANSI escape sequences). This code is public
51# domain - there is no license except that you must leave this header.
52#
53# Copyright (C) 2008 Brian Nez <thedude at bri1 dot com>
54
55
56def parsecolor(color):
57    """SGR parameter string for the specified color name."""
58    matches = re.match(r"color(?P<color>[0-9]+)"
59                       r"|(?P<rgb>rgb(?P<red>[0-5])(?P<green>[0-5])(?P<blue>[0-5]))"
60                       r"|gray(?P<gray>[0-9]+)", color)
61    if not matches:
62        return C.COLOR_CODES[color]
63    if matches.group('color'):
64        return u'38;5;%d' % int(matches.group('color'))
65    if matches.group('rgb'):
66        return u'38;5;%d' % (16 + 36 * int(matches.group('red')) +
67                             6 * int(matches.group('green')) +
68                             int(matches.group('blue')))
69    if matches.group('gray'):
70        return u'38;5;%d' % (232 + int(matches.group('gray')))
71
72
73def stringc(text, color, wrap_nonvisible_chars=False):
74    """String in color."""
75
76    if ANSIBLE_COLOR:
77        color_code = parsecolor(color)
78        fmt = u"\033[%sm%s\033[0m"
79        if wrap_nonvisible_chars:
80            # This option is provided for use in cases when the
81            # formatting of a command line prompt is needed, such as
82            # `ansible-console`. As said in `readline` sources:
83            # readline/display.c:321
84            # /* Current implementation:
85            #         \001 (^A) start non-visible characters
86            #         \002 (^B) end non-visible characters
87            #    all characters except \001 and \002 (following a \001) are copied to
88            #    the returned string; all characters except those between \001 and
89            #    \002 are assumed to be `visible'. */
90            fmt = u"\001\033[%sm\002%s\001\033[0m\002"
91        return u"\n".join([fmt % (color_code, t) for t in text.split(u'\n')])
92    else:
93        return text
94
95
96def colorize(lead, num, color):
97    """ Print 'lead' = 'num' in 'color' """
98    s = u"%s=%-4s" % (lead, str(num))
99    if num != 0 and ANSIBLE_COLOR and color is not None:
100        s = stringc(s, color)
101    return s
102
103
104def hostcolor(host, stats, color=True):
105    if ANSIBLE_COLOR and color:
106        if stats['failures'] != 0 or stats['unreachable'] != 0:
107            return u"%-37s" % stringc(host, C.COLOR_ERROR)
108        elif stats['changed'] != 0:
109            return u"%-37s" % stringc(host, C.COLOR_CHANGED)
110        else:
111            return u"%-37s" % stringc(host, C.COLOR_OK)
112    return u"%-26s" % host
113