]> arthur.barton.de Git - bup.git/blobdiff - lib/bup/helpers.py
username/userfullname: use struct names
[bup.git] / lib / bup / helpers.py
index 9f404afdfc2da4da51b05cde91881cb120f7bd3e..d66b5f17dad32a23331876e3ff58ac2477964388 100644 (file)
@@ -1,5 +1,6 @@
 """Helper functions and classes for bup."""
 
+from __future__ import absolute_import, division
 from collections import namedtuple
 from contextlib import contextmanager
 from ctypes import sizeof, c_void_p
@@ -11,6 +12,10 @@ import hashlib, heapq, math, operator, time, grp, tempfile
 
 from bup import _helpers
 from bup import compat
+# This function should really be in helpers, not in bup.options.  But we
+# want options.py to be standalone so people can include it in other projects.
+from bup.options import _tty_width as tty_width
+
 
 class Nonlocal:
     """Helper to deal with Python scoping issues"""
@@ -24,12 +29,6 @@ sc_arg_max = os.sysconf('SC_ARG_MAX')
 if sc_arg_max == -1:  # "no definite limit" - let's choose 2M
     sc_arg_max = 2 * 1024 * 1024
 
-# This function should really be in helpers, not in bup.options.  But we
-# want options.py to be standalone so people can include it in other projects.
-from bup.options import _tty_width
-tty_width = _tty_width
-
-
 def last(iterable):
     result = None
     for result in iterable:
@@ -100,6 +99,13 @@ def partition(predicate, stream):
     return (leading_matches(), rest())
 
 
+def merge_dict(*xs):
+    result = {}
+    for x in xs:
+        result.update(x)
+    return result
+
+
 def lines_until_sentinel(f, sentinel, ex_type):
     # sentinel must end with \n and must contain only one \n
     while True:
@@ -294,7 +300,7 @@ def _argmax_base(command):
     base_size = 2048
     for c in command:
         base_size += len(command) + 1
-    for k, v in environ.iteritems():
+    for k, v in compat.items(environ):
         base_size += len(k) + len(v) + 2 + sizeof(c_void_p)
     return base_size
 
@@ -351,19 +357,13 @@ def detect_fakeroot():
     return os.getenv("FAKEROOTKEY") != None
 
 
-_warned_about_superuser_detection = None
-def is_superuser():
-    if sys.platform.startswith('cygwin'):
-        if sys.getwindowsversion()[0] > 5:
-            # Sounds like situation is much more complicated here
-            global _warned_about_superuser_detection
-            if not _warned_about_superuser_detection:
-                log("can't detect root status for OS version > 5; assuming not root")
-                _warned_about_superuser_detection = True
-            return False
-        import ctypes
-        return ctypes.cdll.shell32.IsUserAnAdmin()
-    else:
+if sys.platform.startswith('cygwin'):
+    def is_superuser():
+        # https://cygwin.com/ml/cygwin/2015-02/msg00057.html
+        groups = os.getgroups()
+        return 544 in groups or 0 in groups
+else:
+    def is_superuser():
         return os.geteuid() == 0
 
 
@@ -442,7 +442,7 @@ def username():
     global _username
     if not _username:
         uid = os.getuid()
-        _username = pwd_from_uid(uid)[0] or 'user%d' % uid
+        _username = pwd_from_uid(uid).pw_name or b'user%d' % uid
     return _username
 
 
@@ -454,9 +454,9 @@ def userfullname():
         uid = os.getuid()
         entry = pwd_from_uid(uid)
         if entry:
-            _userfullname = entry[4].split(',')[0] or entry[0]
+            _userfullname = entry.pw_gecos.split(b',')[0] or entry.pw_name
         if not _userfullname:
-            _userfullname = 'user%d' % uid
+            _userfullname = b'user%d' % uid
     return _userfullname
 
 
@@ -481,9 +481,9 @@ def format_filesize(size):
     size = float(size)
     if size < unit:
         return "%d" % (size)
-    exponent = int(math.log(size) / math.log(unit))
+    exponent = int(math.log(size) // math.log(unit))
     size_prefix = "KMGTPE"[exponent - 1]
-    return "%.1f%s" % (size / math.pow(unit, exponent), size_prefix)
+    return "%.1f%s" % (size // math.pow(unit, exponent), size_prefix)
 
 
 class NotOk(Exception):
@@ -820,7 +820,7 @@ if _mincore:
         pref_chunk_size = 64 * 1024 * 1024
         chunk_size = sc_page_size
         if (sc_page_size < pref_chunk_size):
-            chunk_size = sc_page_size * (pref_chunk_size / sc_page_size)
+            chunk_size = sc_page_size * (pref_chunk_size // sc_page_size)
         _fmincore_chunk_size = chunk_size
 
     def fmincore(fd):
@@ -832,13 +832,13 @@ if _mincore:
             return bytearray(0)
         if not _fmincore_chunk_size:
             _set_fmincore_chunk_size()
-        pages_per_chunk = _fmincore_chunk_size / sc_page_size;
-        page_count = (st.st_size + sc_page_size - 1) / sc_page_size;
-        chunk_count = page_count / _fmincore_chunk_size
+        pages_per_chunk = _fmincore_chunk_size // sc_page_size;
+        page_count = (st.st_size + sc_page_size - 1) // sc_page_size;
+        chunk_count = page_count // _fmincore_chunk_size
         if chunk_count < 1:
             chunk_count = 1
         result = bytearray(page_count)
-        for ci in xrange(chunk_count):
+        for ci in compat.range(chunk_count):
             pos = _fmincore_chunk_size * ci;
             msize = min(_fmincore_chunk_size, st.st_size - pos)
             try:
@@ -848,7 +848,12 @@ if _mincore:
                     # Perhaps the file was a pipe, i.e. "... | bup split ..."
                     return None
                 raise ex
-            _mincore(m, msize, 0, result, ci * pages_per_chunk);
+            try:
+                _mincore(m, msize, 0, result, ci * pages_per_chunk)
+            except OSError as ex:
+                if ex.errno == errno.ENOSYS:
+                    return None
+                raise
         return result
 
 
@@ -956,15 +961,15 @@ def columnate(l, prefix):
         return ""
     l = l[:]
     clen = max(len(s) for s in l)
-    ncols = (tty_width() - len(prefix)) / (clen + 2)
+    ncols = (tty_width() - len(prefix)) // (clen + 2)
     if ncols <= 1:
         ncols = 1
         clen = 0
     cols = []
     while len(l) % ncols:
         l.append('')
-    rows = len(l)/ncols
-    for s in range(0, len(l), rows):
+    rows = len(l) // ncols
+    for s in compat.range(0, len(l), rows):
         cols.append(l[s:s+rows])
     out = ''
     for row in zip(*cols):
@@ -1056,7 +1061,7 @@ def path_components(path):
     Example:
       '/home/foo' -> [('', '/'), ('home', '/home'), ('foo', '/home/foo')]"""
     if not path.startswith('/'):
-        raise Exception, 'path must start with "/": %s' % path
+        raise Exception('path must start with "/": %s' % path)
     # Since we assume path startswith('/'), we can skip the first element.
     result = [('', '/')]
     norm_path = os.path.abspath(path)
@@ -1176,7 +1181,7 @@ else:
         return x
 
 
-_some_invalid_save_parts_rx = re.compile(r'[[ ~^:?*\\]|\.\.|//|@{')
+_some_invalid_save_parts_rx = re.compile(r'[\[ ~^:?*\\]|\.\.|//|@{')
 
 def valid_save_name(name):
     # Enforce a superset of the restrictions in git-check-ref-format(1)