--- /dev/null
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+@author: Frank Brehm
+@contact: frank.brehm@pixelpark.com
+@copyright: © 2018 by Frank Brehm, Berlin
+@summary: The module for common used functions.
+"""
+
+# Standard modules
+import sys
+import os
+import logging
+import re
+import pprint
+import platform
+import locale
+
+# Third party modules
+import six
+
+# Own modules
+
+__version__ = '0.5.3'
+
+LOG = logging.getLogger(__name__)
+
+RE_YES = re.compile(r'^\s*(?:y(?:es)?|true)\s*$', re.IGNORECASE)
+RE_NO = re.compile(r'^\s*(?:no?|false|off)\s*$', re.IGNORECASE)
+PAT_TO_BOOL_TRUE = locale.nl_langinfo(locale.YESEXPR)
+RE_TO_BOOL_TRUE = re.compile(PAT_TO_BOOL_TRUE)
+PAT_TO_BOOL_FALSE = locale.nl_langinfo(locale.NOEXPR)
+RE_TO_BOOL_FALSE = re.compile(PAT_TO_BOOL_FALSE)
+
+RE_DOT = re.compile(r'\.')
+RE_DOT_AT_END = re.compile(r'(\.)*$')
+RE_DECIMAL = re.compile(r'^\d+$')
+RE_IPV4_PTR = re.compile(r'\.in-addr\.arpa\.$', re.IGNORECASE)
+RE_IPV6_PTR = re.compile(r'\.ip6\.arpa\.$', re.IGNORECASE)
+
+
+# =============================================================================
+def pp(value, indent=4, width=99, depth=None):
+ """
+ Returns a pretty print string of the given value.
+
+ @return: pretty print string
+ @rtype: str
+ """
+
+ pretty_printer = pprint.PrettyPrinter(
+ indent=indent, width=width, depth=depth)
+ return pretty_printer.pformat(value)
+
+
+# =============================================================================
+def terminal_can_colors(debug=False):
+ """
+ Method to detect, whether the current terminal (stdout and stderr)
+ is able to perform ANSI color sequences.
+
+ @return: both stdout and stderr can perform ANSI color sequences
+ @rtype: bool
+
+ """
+
+ cur_term = ''
+ if 'TERM' in os.environ:
+ cur_term = os.environ['TERM'].lower().strip()
+
+ colored_term_list = (
+ r'ansi',
+ r'linux.*',
+ r'screen.*',
+ r'[xeak]term.*',
+ r'gnome.*',
+ r'rxvt.*',
+ r'interix',
+ )
+ term_pattern = r'^(?:' + r'|'.join(colored_term_list) + r')$'
+ re_term = re.compile(term_pattern)
+
+ ansi_term = False
+ env_term_has_colors = False
+
+ if cur_term:
+ if cur_term == 'ansi':
+ env_term_has_colors = True
+ ansi_term = True
+ elif re_term.search(cur_term):
+ env_term_has_colors = True
+ if debug:
+ sys.stderr.write(
+ "ansi_term: %r, env_term_has_colors: %r\n" % (
+ ansi_term, env_term_has_colors))
+
+ has_colors = False
+ if env_term_has_colors:
+ has_colors = True
+ for handle in [sys.stdout, sys.stderr]:
+ if (hasattr(handle, "isatty") and handle.isatty()):
+ if debug:
+ sys.stderr.write("%s is a tty.\n" % (handle.name))
+ if (platform.system() == 'Windows' and not ansi_term):
+ if debug:
+ sys.stderr.write("platform is Windows and not ansi_term.\n")
+ has_colors = False
+ else:
+ if debug:
+ sys.stderr.write("%s is not a tty.\n" % (handle.name))
+ if ansi_term:
+ pass
+ else:
+ has_colors = False
+
+ return has_colors
+
+
+# =============================================================================
+def to_bool(value):
+ """
+ Converter from string to boolean values (e.g. from configurations)
+ """
+
+ if not value:
+ return False
+
+ try:
+ v_int = int(value)
+ except ValueError:
+ pass
+ except TypeError:
+ pass
+ else:
+ if v_int == 0:
+ return False
+ else:
+ return True
+
+ global PAT_TO_BOOL_TRUE
+ global RE_TO_BOOL_TRUE
+ global PAT_TO_BOOL_FALSE
+ global RE_TO_BOOL_FALSE
+
+ c_yes_expr = locale.nl_langinfo(locale.YESEXPR)
+ if c_yes_expr != PAT_TO_BOOL_TRUE:
+ PAT_TO_BOOL_TRUE = c_yes_expr
+ RE_TO_BOOL_TRUE = re.compile(PAT_TO_BOOL_TRUE)
+ # LOG.debug("Current pattern for 'yes': %r.", c_yes_expr)
+
+ c_no_expr = locale.nl_langinfo(locale.NOEXPR)
+ if c_no_expr != PAT_TO_BOOL_FALSE:
+ PAT_TO_BOOL_FALSE = c_no_expr
+ RE_TO_BOOL_FALSE = re.compile(PAT_TO_BOOL_FALSE)
+ # LOG.debug("Current pattern for 'no': %r.", c_no_expr)
+
+ v_str = ''
+ if isinstance(value, str):
+ v_str = value
+ if six.PY2:
+ if isinstance(value, unicode): # noqa
+ v_str = value.encode('utf-8')
+ elif six.PY3 and isinstance(value, bytes):
+ v_str = value.decode('utf-8')
+ else:
+ v_str = str(value)
+
+ match = RE_YES.search(v_str)
+ if match:
+ return True
+ match = RE_TO_BOOL_TRUE.search(v_str)
+ if match:
+ return True
+
+ match = RE_NO.search(v_str)
+ if match:
+ return False
+ match = RE_TO_BOOL_FALSE.search(v_str)
+ if match:
+ return False
+
+ return bool(value)
+
+
+# =============================================================================
+def to_unicode(obj, encoding='utf-8'):
+
+ do_decode = False
+ if six.PY2:
+ if isinstance(obj, str):
+ do_decode = True
+ else:
+ if isinstance(obj, bytes):
+ do_decode = True
+
+ if do_decode:
+ obj = obj.decode(encoding)
+
+ return obj
+
+
+# =============================================================================
+def to_utf8(obj):
+
+ return encode_or_bust(obj, 'utf-8')
+
+
+# =============================================================================
+def encode_or_bust(obj, encoding='utf-8'):
+
+ do_encode = False
+ if six.PY2:
+ if isinstance(obj, unicode): # noqa
+ do_encode = True
+ else:
+ if isinstance(obj, str):
+ do_encode = True
+
+ if do_encode:
+ obj = obj.encode(encoding)
+
+ return obj
+
+
+# =============================================================================
+def to_bytes(obj, encoding='utf-8'):
+ "Wrapper for encode_or_bust()"
+
+ return encode_or_bust(obj, encoding)
+
+
+# =============================================================================
+def to_str(obj, encoding='utf-8'):
+ """
+ Transformes the given string-like object into the str-type according
+ to the current Python version.
+ """
+
+ if six.PY2:
+ return encode_or_bust(obj, encoding)
+ else:
+ return to_unicode(obj, encoding)
+
+
+# =============================================================================
+def caller_search_path():
+ """
+ Builds a search path for executables from environment $PATH
+ including some standard paths.
+
+ @return: all existing search paths
+ @rtype: list
+ """
+
+ path_list = []
+ search_path = os.environ['PATH']
+ if not search_path:
+ search_path = os.defpath
+
+ search_path_list = [
+ '/opt/PPlocal/bin',
+ ]
+
+ for d in search_path.split(os.pathsep):
+ search_path_list.append(d)
+
+ default_path = [
+ '/bin',
+ '/usr/bin',
+ '/usr/local/bin',
+ '/sbin',
+ '/usr/sbin',
+ '/usr/local/sbin',
+ '/usr/ucb',
+ '/usr/sfw/bin',
+ '/opt/csw/bin',
+ '/usr/openwin/bin',
+ '/usr/ccs/bin',
+ ]
+
+ for d in default_path:
+ search_path_list.append(d)
+
+ for d in search_path_list:
+ if not os.path.exists(d):
+ continue
+ if not os.path.isdir(d):
+ continue
+ d_abs = os.path.realpath(d)
+ if d_abs not in path_list:
+ path_list.append(d_abs)
+
+ return path_list
+
+# =============================================================================
+def compare_fqdn(x, y):
+
+ # LOG.debug("Comparing {!r} <=> {!r}.".format(x, y))
+
+ # First check for None values
+ if x is None and y is None:
+ return 0
+ if x is None:
+ return -1
+ if y is None:
+ return 1
+
+ # Check for empty FQDNs
+ xs = str(x).strip().lower()
+ ys = str(y).strip().lower()
+
+ if xs == '' and ys == '':
+ return 0
+ if xs == '':
+ return -1
+ if ys == '':
+ return 1
+
+ # Ensure a dot at end
+ xs = RE_DOT_AT_END.sub('.', xs)
+ ys = RE_DOT_AT_END.sub('.', ys)
+
+ if xs == ys:
+ return 0
+
+ # Reverse IPv4 zones first, then reverse IPv6 zones
+ if RE_IPV4_PTR.search(xs):
+ if not RE_IPV4_PTR.search(ys):
+ return -1
+ elif RE_IPV4_PTR.search(ys):
+ if not RE_IPV4_PTR.search(xs):
+ return 1
+ elif RE_IPV6_PTR.search(xs):
+ if not RE_IPV6_PTR.search(ys):
+ return -1
+ elif RE_IPV6_PTR.search(ys):
+ if not RE_IPV6_PTR.search(xs):
+ return 1
+
+ return compare_fqdn_tokens(xs, ys)
+
+# =============================================================================
+def compare_fqdn_tokens(xs, ys):
+
+ xa = RE_DOT.split(xs)
+ xa.reverse()
+ xa.pop(0)
+
+ ya = RE_DOT.split(ys)
+ ya.reverse()
+ ya.pop(0)
+
+ # Compare token from the last to the first
+ nr_tokens = min(len(xa), len(ya))
+ while nr_tokens > 0:
+ token_x = xa.pop(0)
+ token_y = ya.pop(0)
+ if RE_DECIMAL.match(token_x) and RE_DECIMAL.match(token_y):
+ num_x = int(token_x)
+ num_y = int(token_y)
+ if num_x < num_y:
+ return -1
+ elif num_x > num_y:
+ return 1
+ else:
+ if token_x < token_y:
+ return -1
+ elif token_x > token_y:
+ return 1
+ nr_tokens -= 1
+
+ if len(xa):
+ return 1
+ if len(ya):
+ return -1
+
+ return 0
+
+# =============================================================================
+
+if __name__ == "__main__":
+
+ pass
+
+# =============================================================================
+
+# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4