]> arthur.barton.de Git - bup.git/blobdiff - lib/bup/ls.py
ls: make multiple arguments match real ls
[bup.git] / lib / bup / ls.py
index 1de8e6b32bd35faabab6184240da632c57068c27..2dbe99d67ad5fd078e1f62b41f07e4423df83f28 100644 (file)
@@ -1,14 +1,18 @@
 """Common code for listing files from a bup repository."""
 
-from __future__ import print_function
+from __future__ import absolute_import
+from binascii import hexlify
 from itertools import chain
 from stat import S_ISDIR, S_ISLNK
-import copy, locale, os.path, stat, sys, xstat
+import copy, locale, os.path, stat, sys
+import posixpath
 
-from bup import metadata, options, vfs2 as vfs
+from bup import metadata, options, vfs, xstat
+from bup.compat import argv_bytes
+from bup.io import path_msg
 from bup.options import Options
 from bup.repo import LocalRepo, RemoteRepo
-from helpers import columnate, istty1, last, log
+from bup.helpers import columnate, istty1, last, log
 
 def item_hash(item, tree_for_commit):
     """If the item is a Commit, return its commit oid, otherwise return
@@ -26,28 +30,29 @@ def item_info(item, name,
               classification = None,
               numeric_ids = False,
               human_readable = False):
-    """Return a string containing the information to display for the VFS
+    """Return bytes containing the information to display for the VFS
     item.  Classification may be "all", "type", or None.
 
     """
-    result = ''
+    result = b''
     if show_hash:
         oid = item_hash(item, commit_hash)
-        result += '%s ' % (oid.encode('hex') if oid
-                           else '0000000000000000000000000000000000000000')
+        result += b'%s ' % (hexlify(oid) if oid
+                            else b'0000000000000000000000000000000000000000')
     if long_fmt:
         meta = item.meta.copy()
         meta.path = name
         # FIXME: need some way to track fake vs real meta items?
-        result += metadata.summary_str(meta,
-                                       numeric_ids=numeric_ids,
-                                       classification=classification,
-                                       human_readable=human_readable)
+        result += metadata.summary_bytes(meta,
+                                         numeric_ids=numeric_ids,
+                                         classification=classification,
+                                         human_readable=human_readable)
     else:
         result += name
         if classification:
-            result += xstat.classification_str(vfs.item_mode(item),
-                                               classification == 'all')
+            cls = xstat.classification_str(vfs.item_mode(item),
+                                           classification == 'all')
+            result += cls.encode('ascii')
     return result
 
 
@@ -67,18 +72,18 @@ human-readable    print human readable file sizes (i.e. 3.9K, 4.7M)
 n,numeric-ids list numeric IDs (user, group, etc.) rather than names
 """
 
-def opts_from_cmdline(args, onabort=None):
+def opts_from_cmdline(args, onabort=None, pwd=b'/'):
     """Parse ls command line arguments and return a dictionary of ls
     options, agumented with "classification", "long_listing",
     "paths", and "show_hidden".
 
     """
     if onabort:
-        opt, flags, extra = Options(optspec, onabort=onabort).parse(args)
+        opt, flags, extra = Options(optspec, onabort=onabort).parse_bytes(args)
     else:
-        opt, flags, extra = Options(optspec).parse(args)
+        opt, flags, extra = Options(optspec).parse_bytes(args)
 
-    opt.paths = extra or ('/',)
+    opt.paths = [argv_bytes(x) for x in extra] or (pwd,)
     opt.long_listing = opt.l
     opt.classification = None
     opt.show_hidden = None
@@ -94,7 +99,7 @@ def opts_from_cmdline(args, onabort=None):
             opt.show_hidden = 'almost'
     return opt
 
-def within_repo(repo, opt):
+def within_repo(repo, opt, out, pwd=b''):
 
     if opt.commit_hash:
         opt.hash = True
@@ -110,18 +115,23 @@ def within_repo(repo, opt):
 
     ret = 0
     pending = []
-    for path in opt.paths:
+    last_n = len(opt.paths) - 1
+    for n, printpath in enumerate(opt.paths):
+        path = posixpath.join(pwd, printpath)
         try:
+            if last_n > 0:
+                out.write(b'%s:\n' % printpath)
+
             if opt.directory:
-                resolved = vfs.lresolve(repo, path)
+                resolved = vfs.resolve(repo, path, follow=False)
             else:
                 resolved = vfs.try_resolve(repo, path)
 
             leaf_name, leaf_item = resolved[-1]
             if not leaf_item:
                 log('error: cannot access %r in %r\n'
-                    % ('/'.join(name for name, item in resolved),
-                       path))
+                    % ('/'.join(path_msg(name) for name, item in resolved),
+                       path_msg(path)))
                 ret = 1
                 continue
             if not opt.directory and S_ISDIR(vfs.item_mode(leaf_item)):
@@ -129,11 +139,11 @@ def within_repo(repo, opt):
                 if opt.show_hidden == 'all':
                     # Match non-bup "ls -a ... /".
                     parent = resolved[-2] if len(resolved) > 1 else resolved[0]
-                    items = chain(items, (('..', parent[1]),))
+                    items = chain(items, ((b'..', parent[1]),))
                 for sub_name, sub_item in sorted(items, key=lambda x: x[0]):
-                    if opt.show_hidden != 'all' and sub_name == '.':
+                    if opt.show_hidden != 'all' and sub_name == b'.':
                         continue
-                    if sub_name.startswith('.') and \
+                    if sub_name.startswith(b'.') and \
                        opt.show_hidden not in ('almost', 'all'):
                         continue
                     if opt.l:
@@ -146,7 +156,8 @@ def within_repo(repo, opt):
                     if not opt.long_listing and istty1:
                         pending.append(line)
                     else:
-                        print(line)
+                        out.write(line)
+                        out.write(b'\n')
             else:
                 leaf_item = vfs.augment_item_meta(repo, leaf_item,
                                                   include_size=True)
@@ -154,18 +165,23 @@ def within_repo(repo, opt):
                 if not opt.long_listing and istty1:
                     pending.append(line)
                 else:
-                    print(line)
+                    out.write(line)
+                    out.write(b'\n')
         except vfs.IOError as ex:
             log('bup: %s\n' % ex)
             ret = 1
 
-    if pending:
-        sys.stdout.write(columnate(pending, ''))
+        if pending:
+            out.write(columnate(pending, b''))
+            pending = []
+
+        if n < last_n:
+            out.write(b'\n')
 
     return ret
 
-def via_cmdline(args, onabort=None):
-    """Output a listing of a file or directory in the bup repository.
+def via_cmdline(args, out=None, onabort=None):
+    """Write a listing of a file or directory in the bup repository to out.
 
     When a long listing is not requested and stdout is attached to a
     tty, the output is formatted in columns. When not attached to tty
@@ -173,6 +189,7 @@ def via_cmdline(args, onabort=None):
     file is listed per line.
 
     """
+    assert out
     opt = opts_from_cmdline(args, onabort=onabort)
-    return within_repo(RemoteRepo(opt.remote) if opt.remote else LocalRepo(),
-                       opt)
+    repo = RemoteRepo(argv_bytes(opt.remote)) if opt.remote else LocalRepo()
+    return within_repo(repo, opt, out)