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
13 def item_info(item, name,
16 classification = None,
18 human_readable = False):
19 """Return a string containing the information to display for the VFS
20 item. Classification may be "all", "type", or None.
24 if show_hash and hasattr(item, 'oid'):
25 result += '%s ' % item.oid.encode('hex')
27 meta = item.meta.copy()
29 # FIXME: need some way to track fake vs real meta items?
30 result += metadata.summary_str(meta,
31 numeric_ids=numeric_ids,
32 classification=classification,
33 human_readable=human_readable)
37 result += xstat.classification_str(item.meta.mode,
38 classification == 'all')
45 s,hash show hash for each file
46 a,all show hidden files
47 A,almost-all show hidden files except . and ..
48 l use a detailed, long listing format
49 d,directory show directories, not contents; don't follow symlinks
50 F,classify append type indicator: dir/ sym@ fifo| sock= exec*
51 file-type append type indicator: dir/ sym@ fifo| sock=
52 human-readable print human readable file sizes (i.e. 3.9K, 4.7M)
53 n,numeric-ids list numeric IDs (user, group, etc.) rather than names
56 def do_ls(args, default='.', onabort=None, spec_prefix=''):
57 """Output a listing of a file or directory in the bup repository.
59 When a long listing is not requested and stdout is attached to a
60 tty, the output is formatted in columns. When not attached to tty
61 (for example when the output is piped to another command), one
62 file is listed per line.
66 o = options.Options(optspec % spec_prefix, onabort=onabort)
68 o = options.Options(optspec % spec_prefix)
69 (opt, flags, extra) = o.parse(args)
71 # Handle order-sensitive options.
75 (option, parameter) = flag
76 if option in ('-F', '--classify'):
77 classification = 'all'
78 elif option == '--file-type':
79 classification = 'type'
80 elif option in ('-a', '--all'):
82 elif option in ('-A', '--almost-all'):
83 show_hidden = 'almost'
85 def item_line(item, name):
86 return item_info(item, name,
89 classification = classification,
90 numeric_ids = opt.numeric_ids,
91 human_readable = opt.human_readable)
96 for path in (extra or [default]):
99 resolved = vfs.lresolve(repo, path)
101 # FIXME: deal with invalid symlinks i.e. old vfs try_resolve
102 resolved = vfs.resolve(repo, path)
104 leaf_name, leaf_item = resolved[-1]
106 log('error: cannot access %r in %r\n'
107 % ('/'.join(name for name, item in resolved),
111 if not opt.directory and S_ISDIR(vfs.item_mode(leaf_item)):
112 items = vfs.contents(repo, leaf_item)
113 if show_hidden == 'all':
114 # Match non-bup "ls -a ... /".
115 parent = resolved[-2] if len(resolved) > 1 else resolved[0]
116 items = chain(items, (('..', parent[1]),))
118 items = ((x[0], vfs.augment_item_meta(repo, x[1],
121 for sub_name, sub_item in sorted(items, key=lambda x: x[0]):
122 if show_hidden != 'all' and sub_name == '.':
124 if sub_name.startswith('.') and \
125 show_hidden not in ('almost', 'all'):
127 line = item_line(sub_item, sub_name)
128 pending.append(line) if not opt.l and istty1 else print(line)
130 leaf_item = vfs.augment_item_meta(repo, leaf_item,
132 line = item_line(leaf_item, os.path.normpath(path))
133 pending.append(line) if not opt.l and istty1 else print(line)
134 except vfs.IOError as ex:
135 log('bup: %s\n' % ex)
139 sys.stdout.write(columnate(pending, ''))