1 """Common code for listing files from a bup repository."""
3 from __future__ import print_function
4 from itertools import chain
5 from stat import S_ISDIR, S_ISLNK
6 import copy, locale, os.path, stat, sys, xstat
8 from bup import metadata, options, vfs2 as vfs
9 from bup.repo import LocalRepo
10 from helpers import columnate, istty1, last, log
12 def item_hash(item, tree_for_commit):
13 """If the item is a Commit, return its commit oid, otherwise return
14 the item's oid, if it has one.
17 if tree_for_commit and isinstance(item, vfs.Commit):
19 return getattr(item, 'oid', None)
21 def item_info(item, name,
25 classification = None,
27 human_readable = False):
28 """Return a string containing the information to display for the VFS
29 item. Classification may be "all", "type", or None.
34 oid = item_hash(item, commit_hash)
35 result += '%s ' % (oid.encode('hex') if oid
36 else '0000000000000000000000000000000000000000')
38 meta = item.meta.copy()
40 # FIXME: need some way to track fake vs real meta items?
41 result += metadata.summary_str(meta,
42 numeric_ids=numeric_ids,
43 classification=classification,
44 human_readable=human_readable)
48 result += xstat.classification_str(item.meta.mode,
49 classification == 'all')
56 s,hash show hash for each file
57 commit-hash show commit hash instead of tree for commits (implies -s)
58 a,all show hidden files
59 A,almost-all show hidden files except . and ..
60 l use a detailed, long listing format
61 d,directory show directories, not contents; don't follow symlinks
62 F,classify append type indicator: dir/ sym@ fifo| sock= exec*
63 file-type append type indicator: dir/ sym@ fifo| sock=
64 human-readable print human readable file sizes (i.e. 3.9K, 4.7M)
65 n,numeric-ids list numeric IDs (user, group, etc.) rather than names
68 def do_ls(args, default='.', onabort=None, spec_prefix=''):
69 """Output a listing of a file or directory in the bup repository.
71 When a long listing is not requested and stdout is attached to a
72 tty, the output is formatted in columns. When not attached to tty
73 (for example when the output is piped to another command), one
74 file is listed per line.
78 o = options.Options(optspec % spec_prefix, onabort=onabort)
80 o = options.Options(optspec % spec_prefix)
81 (opt, flags, extra) = o.parse(args)
83 # Handle order-sensitive options.
87 (option, parameter) = flag
88 if option in ('-F', '--classify'):
89 classification = 'all'
90 elif option == '--file-type':
91 classification = 'type'
92 elif option in ('-a', '--all'):
94 elif option in ('-A', '--almost-all'):
95 show_hidden = 'almost'
100 def item_line(item, name):
101 return item_info(item, name,
102 show_hash = opt.hash,
103 commit_hash=opt.commit_hash,
105 classification = classification,
106 numeric_ids = opt.numeric_ids,
107 human_readable = opt.human_readable)
112 for path in (extra or [default]):
115 resolved = vfs.lresolve(repo, path)
117 # FIXME: deal with invalid symlinks i.e. old vfs try_resolve
118 resolved = vfs.resolve(repo, path)
120 leaf_name, leaf_item = resolved[-1]
122 log('error: cannot access %r in %r\n'
123 % ('/'.join(name for name, item in resolved),
127 if not opt.directory and S_ISDIR(vfs.item_mode(leaf_item)):
128 items = vfs.contents(repo, leaf_item)
129 if show_hidden == 'all':
130 # Match non-bup "ls -a ... /".
131 parent = resolved[-2] if len(resolved) > 1 else resolved[0]
132 items = chain(items, (('..', parent[1]),))
134 items = ((x[0], vfs.augment_item_meta(repo, x[1],
137 for sub_name, sub_item in sorted(items, key=lambda x: x[0]):
138 if show_hidden != 'all' and sub_name == '.':
140 if sub_name.startswith('.') and \
141 show_hidden not in ('almost', 'all'):
143 line = item_line(sub_item, sub_name)
144 pending.append(line) if not opt.l and istty1 else print(line)
146 leaf_item = vfs.augment_item_meta(repo, leaf_item,
148 line = item_line(leaf_item, os.path.normpath(path))
149 pending.append(line) if not opt.l and istty1 else print(line)
150 except vfs.IOError as ex:
151 log('bup: %s\n' % ex)
155 sys.stdout.write(columnate(pending, ''))