]> arthur.barton.de Git - bup.git/blob - lib/bup/ls.py
Support remote listings: bup ls -r ...
[bup.git] / lib / bup / ls.py
1 """Common code for listing files from a bup repository."""
2
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
7
8 from bup import metadata, options, vfs2 as vfs
9 from bup.repo import LocalRepo, RemoteRepo
10 from helpers import columnate, istty1, last, log
11
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.
15
16     """
17     if tree_for_commit and isinstance(item, vfs.Commit):
18         return item.coid
19     return getattr(item, 'oid', None)
20
21 def item_info(item, name,
22               show_hash = False,
23               commit_hash=False,
24               long_fmt = False,
25               classification = None,
26               numeric_ids = False,
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.
30
31     """
32     result = ''
33     if show_hash:
34         oid = item_hash(item, commit_hash)
35         result += '%s ' % (oid.encode('hex') if oid
36                            else '0000000000000000000000000000000000000000')
37     if long_fmt:
38         meta = item.meta.copy()
39         meta.path = name
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)
45     else:
46         result += name
47         if classification:
48             result += xstat.classification_str(vfs.item_mode(item),
49                                                classification == 'all')
50     return result
51
52
53 optspec = """
54 %sls [-r host:path] [-l] [-d] [-F] [-a] [-A] [-s] [-n] [path...]
55 --
56 r,remote=   remote repository path
57 s,hash   show hash for each file
58 commit-hash show commit hash instead of tree for commits (implies -s)
59 a,all    show hidden files
60 A,almost-all    show hidden files except . and ..
61 l        use a detailed, long listing format
62 d,directory show directories, not contents; don't follow symlinks
63 F,classify append type indicator: dir/ sym@ fifo| sock= exec*
64 file-type append type indicator: dir/ sym@ fifo| sock=
65 human-readable    print human readable file sizes (i.e. 3.9K, 4.7M)
66 n,numeric-ids list numeric IDs (user, group, etc.) rather than names
67 """
68
69 def do_ls(args, default='.', onabort=None, spec_prefix=''):
70     """Output a listing of a file or directory in the bup repository.
71
72     When a long listing is not requested and stdout is attached to a
73     tty, the output is formatted in columns. When not attached to tty
74     (for example when the output is piped to another command), one
75     file is listed per line.
76
77     """
78     if onabort:
79         o = options.Options(optspec % spec_prefix, onabort=onabort)
80     else:
81         o = options.Options(optspec % spec_prefix)
82     (opt, flags, extra) = o.parse(args)
83
84     # Handle order-sensitive options.
85     classification = None
86     show_hidden = None
87     for flag in flags:
88         (option, parameter) = flag
89         if option in ('-F', '--classify'):
90             classification = 'all'
91         elif option == '--file-type':
92             classification = 'type'
93         elif option in ('-a', '--all'):
94             show_hidden = 'all'
95         elif option in ('-A', '--almost-all'):
96             show_hidden = 'almost'
97
98     if opt.commit_hash:
99         opt.hash = True
100
101     def item_line(item, name):
102         return item_info(item, name,
103                          show_hash = opt.hash,
104                          commit_hash=opt.commit_hash,
105                          long_fmt = opt.l,
106                          classification = classification,
107                          numeric_ids = opt.numeric_ids,
108                          human_readable = opt.human_readable)
109
110     repo = RemoteRepo(opt.remote) if opt.remote else LocalRepo()
111     ret = 0
112     pending = []
113     for path in (extra or [default]):
114         try:
115             if opt.directory:
116                 resolved = vfs.lresolve(repo, path)
117             else:
118                 resolved = vfs.try_resolve(repo, path)
119
120             leaf_name, leaf_item = resolved[-1]
121             if not leaf_item:
122                 log('error: cannot access %r in %r\n'
123                     % ('/'.join(name for name, item in resolved),
124                        path))
125                 ret = 1
126                 continue
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]),))
133                 for sub_name, sub_item in sorted(items, key=lambda x: x[0]):
134                     if show_hidden != 'all' and sub_name == '.':
135                         continue
136                     if sub_name.startswith('.') and \
137                        show_hidden not in ('almost', 'all'):
138                         continue
139                     if opt.l:
140                         sub_item = vfs.ensure_item_has_metadata(repo, sub_item,
141                                                                 include_size=True)
142                     else:
143                         sub_item = vfs.augment_item_meta(repo, sub_item,
144                                                          include_size=True)
145                     line = item_line(sub_item, sub_name)
146                     pending.append(line) if not opt.l and istty1 else print(line)
147             else:
148                 leaf_item = vfs.augment_item_meta(repo, leaf_item,
149                                                   include_size=True)
150                 line = item_line(leaf_item, os.path.normpath(path))
151                 pending.append(line) if not opt.l and istty1 else print(line)
152         except vfs.IOError as ex:
153             log('bup: %s\n' % ex)
154             ret = 1
155
156     if pending:
157         sys.stdout.write(columnate(pending, ''))
158
159     return ret