1 """Common code for listing files from a bup repository."""
3 from __future__ import absolute_import
4 from binascii import hexlify
5 from itertools import chain
6 from stat import S_ISDIR, S_ISLNK
7 import copy, locale, os.path, stat, sys
9 from bup import metadata, options, vfs, xstat
10 from bup.compat import argv_bytes
11 from bup.io import path_msg
12 from bup.options import Options
13 from bup.repo import LocalRepo, RemoteRepo
14 from bup.helpers import columnate, istty1, last, log
16 def item_hash(item, tree_for_commit):
17 """If the item is a Commit, return its commit oid, otherwise return
18 the item's oid, if it has one.
21 if tree_for_commit and isinstance(item, vfs.Commit):
23 return getattr(item, 'oid', None)
25 def item_info(item, name,
29 classification = None,
31 human_readable = False):
32 """Return bytes containing the information to display for the VFS
33 item. Classification may be "all", "type", or None.
38 oid = item_hash(item, commit_hash)
39 result += b'%s ' % (hexlify(oid) if oid
40 else b'0000000000000000000000000000000000000000')
42 meta = item.meta.copy()
44 # FIXME: need some way to track fake vs real meta items?
45 result += metadata.summary_bytes(meta,
46 numeric_ids=numeric_ids,
47 classification=classification,
48 human_readable=human_readable)
52 cls = xstat.classification_str(vfs.item_mode(item),
53 classification == 'all')
54 result += cls.encode('ascii')
59 bup ls [-r host:path] [-l] [-d] [-F] [-a] [-A] [-s] [-n] [path...]
61 r,remote= remote repository path
62 s,hash show hash for each file
63 commit-hash show commit hash instead of tree for commits (implies -s)
64 a,all show hidden files
65 A,almost-all show hidden files except . and ..
66 l use a detailed, long listing format
67 d,directory show directories, not contents; don't follow symlinks
68 F,classify append type indicator: dir/ sym@ fifo| sock= exec*
69 file-type append type indicator: dir/ sym@ fifo| sock=
70 human-readable print human readable file sizes (i.e. 3.9K, 4.7M)
71 n,numeric-ids list numeric IDs (user, group, etc.) rather than names
74 def opts_from_cmdline(args, onabort=None):
75 """Parse ls command line arguments and return a dictionary of ls
76 options, agumented with "classification", "long_listing",
77 "paths", and "show_hidden".
81 opt, flags, extra = Options(optspec, onabort=onabort).parse(args)
83 opt, flags, extra = Options(optspec).parse(args)
85 opt.paths = [argv_bytes(x) for x in extra] or (b'/',)
86 opt.long_listing = opt.l
87 opt.classification = None
88 opt.show_hidden = None
90 option, parameter = flag
91 if option in ('-F', '--classify'):
92 opt.classification = 'all'
93 elif option == '--file-type':
94 opt.classification = 'type'
95 elif option in ('-a', '--all'):
96 opt.show_hidden = 'all'
97 elif option in ('-A', '--almost-all'):
98 opt.show_hidden = 'almost'
101 def within_repo(repo, opt, out):
106 def item_line(item, name):
107 return item_info(item, name,
109 commit_hash=opt.commit_hash,
110 long_fmt=opt.long_listing,
111 classification=opt.classification,
112 numeric_ids=opt.numeric_ids,
113 human_readable=opt.human_readable)
117 for path in opt.paths:
120 resolved = vfs.resolve(repo, path, follow=False)
122 resolved = vfs.try_resolve(repo, path)
124 leaf_name, leaf_item = resolved[-1]
126 log('error: cannot access %r in %r\n'
127 % ('/'.join(path_msg(name) for name, item in resolved),
131 if not opt.directory and S_ISDIR(vfs.item_mode(leaf_item)):
132 items = vfs.contents(repo, leaf_item)
133 if opt.show_hidden == 'all':
134 # Match non-bup "ls -a ... /".
135 parent = resolved[-2] if len(resolved) > 1 else resolved[0]
136 items = chain(items, ((b'..', parent[1]),))
137 for sub_name, sub_item in sorted(items, key=lambda x: x[0]):
138 if opt.show_hidden != 'all' and sub_name == b'.':
140 if sub_name.startswith(b'.') and \
141 opt.show_hidden not in ('almost', 'all'):
144 sub_item = vfs.ensure_item_has_metadata(repo, sub_item,
147 sub_item = vfs.augment_item_meta(repo, sub_item,
149 line = item_line(sub_item, sub_name)
150 if not opt.long_listing and istty1:
156 leaf_item = vfs.augment_item_meta(repo, leaf_item,
158 line = item_line(leaf_item, os.path.normpath(path))
159 if not opt.long_listing and istty1:
164 except vfs.IOError as ex:
165 log('bup: %s\n' % ex)
169 out.write(columnate(pending, b''))
173 def via_cmdline(args, out=None, onabort=None):
174 """Write a listing of a file or directory in the bup repository to out.
176 When a long listing is not requested and stdout is attached to a
177 tty, the output is formatted in columns. When not attached to tty
178 (for example when the output is piped to another command), one
179 file is listed per line.
183 opt = opts_from_cmdline(args, onabort=onabort)
184 repo = RemoteRepo(argv_bytes(opt.remote)) if opt.remote else LocalRepo()
185 return within_repo(repo, opt, out)