1 """Common code for listing files from a bup repository."""
3 from __future__ import absolute_import, print_function
4 from itertools import chain
5 from stat import S_ISDIR, S_ISLNK
6 import copy, locale, os.path, stat, sys
8 from bup import metadata, options, vfs, xstat
9 from bup.options import Options
10 from bup.repo import LocalRepo, RemoteRepo
11 from bup.helpers import columnate, istty1, last, log
13 def item_hash(item, tree_for_commit):
14 """If the item is a Commit, return its commit oid, otherwise return
15 the item's oid, if it has one.
18 if tree_for_commit and isinstance(item, vfs.Commit):
20 return getattr(item, 'oid', None)
22 def item_info(item, name,
26 classification = None,
28 human_readable = False):
29 """Return a string containing the information to display for the VFS
30 item. Classification may be "all", "type", or None.
35 oid = item_hash(item, commit_hash)
36 result += '%s ' % (oid.encode('hex') if oid
37 else '0000000000000000000000000000000000000000')
39 meta = item.meta.copy()
41 # FIXME: need some way to track fake vs real meta items?
42 result += metadata.summary_str(meta,
43 numeric_ids=numeric_ids,
44 classification=classification,
45 human_readable=human_readable)
49 result += xstat.classification_str(vfs.item_mode(item),
50 classification == 'all')
55 bup ls [-r host:path] [-l] [-d] [-F] [-a] [-A] [-s] [-n] [path...]
57 r,remote= remote repository path
58 s,hash show hash for each file
59 commit-hash show commit hash instead of tree for commits (implies -s)
60 a,all show hidden files
61 A,almost-all show hidden files except . and ..
62 l use a detailed, long listing format
63 d,directory show directories, not contents; don't follow symlinks
64 F,classify append type indicator: dir/ sym@ fifo| sock= exec*
65 file-type append type indicator: dir/ sym@ fifo| sock=
66 human-readable print human readable file sizes (i.e. 3.9K, 4.7M)
67 n,numeric-ids list numeric IDs (user, group, etc.) rather than names
70 def opts_from_cmdline(args, onabort=None):
71 """Parse ls command line arguments and return a dictionary of ls
72 options, agumented with "classification", "long_listing",
73 "paths", and "show_hidden".
77 opt, flags, extra = Options(optspec, onabort=onabort).parse(args)
79 opt, flags, extra = Options(optspec).parse(args)
81 opt.paths = extra or ('/',)
82 opt.long_listing = opt.l
83 opt.classification = None
84 opt.show_hidden = None
86 option, parameter = flag
87 if option in ('-F', '--classify'):
88 opt.classification = 'all'
89 elif option == '--file-type':
90 opt.classification = 'type'
91 elif option in ('-a', '--all'):
92 opt.show_hidden = 'all'
93 elif option in ('-A', '--almost-all'):
94 opt.show_hidden = 'almost'
97 def within_repo(repo, opt):
102 def item_line(item, name):
103 return item_info(item, name,
105 commit_hash=opt.commit_hash,
106 long_fmt=opt.long_listing,
107 classification=opt.classification,
108 numeric_ids=opt.numeric_ids,
109 human_readable=opt.human_readable)
113 for path in opt.paths:
116 resolved = vfs.lresolve(repo, path)
118 resolved = vfs.try_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 opt.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]),))
133 for sub_name, sub_item in sorted(items, key=lambda x: x[0]):
134 if opt.show_hidden != 'all' and sub_name == '.':
136 if sub_name.startswith('.') and \
137 opt.show_hidden not in ('almost', 'all'):
140 sub_item = vfs.ensure_item_has_metadata(repo, sub_item,
143 sub_item = vfs.augment_item_meta(repo, sub_item,
145 line = item_line(sub_item, sub_name)
146 if not opt.long_listing and istty1:
151 leaf_item = vfs.augment_item_meta(repo, leaf_item,
153 line = item_line(leaf_item, os.path.normpath(path))
154 if not opt.long_listing and istty1:
158 except vfs.IOError as ex:
159 log('bup: %s\n' % ex)
163 sys.stdout.write(columnate(pending, ''))
167 def via_cmdline(args, onabort=None):
168 """Output a listing of a file or directory in the bup repository.
170 When a long listing is not requested and stdout is attached to a
171 tty, the output is formatted in columns. When not attached to tty
172 (for example when the output is piped to another command), one
173 file is listed per line.
176 opt = opts_from_cmdline(args, onabort=onabort)
177 return within_repo(RemoteRepo(opt.remote) if opt.remote else LocalRepo(),