]> arthur.barton.de Git - bup.git/blob - lib/bup/ls.py
ls: retrieve all of the available metadata when requested
[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
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 [-a] [path...]
55 --
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
66 """
67
68 def do_ls(args, default='.', onabort=None, spec_prefix=''):
69     """Output a listing of a file or directory in the bup repository.
70
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.
75
76     """
77     if onabort:
78         o = options.Options(optspec % spec_prefix, onabort=onabort)
79     else:
80         o = options.Options(optspec % spec_prefix)
81     (opt, flags, extra) = o.parse(args)
82
83     # Handle order-sensitive options.
84     classification = None
85     show_hidden = None
86     for flag in flags:
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'):
93             show_hidden = 'all'
94         elif option in ('-A', '--almost-all'):
95             show_hidden = 'almost'
96
97     if opt.commit_hash:
98         opt.hash = True
99
100     def item_line(item, name):
101         return item_info(item, name,
102                          show_hash = opt.hash,
103                          commit_hash=opt.commit_hash,
104                          long_fmt = opt.l,
105                          classification = classification,
106                          numeric_ids = opt.numeric_ids,
107                          human_readable = opt.human_readable)
108
109     repo = LocalRepo()
110     ret = 0
111     pending = []
112     for path in (extra or [default]):
113         try:
114             if opt.directory:
115                 resolved = vfs.lresolve(repo, path)
116             else:
117                 resolved = vfs.try_resolve(repo, path)
118
119             leaf_name, leaf_item = resolved[-1]
120             if not leaf_item:
121                 log('error: cannot access %r in %r\n'
122                     % ('/'.join(name for name, item in resolved),
123                        path))
124                 ret = 1
125                 continue
126             if not opt.directory and S_ISDIR(vfs.item_mode(leaf_item)):
127                 items = vfs.contents(repo, leaf_item)
128                 if show_hidden == 'all':
129                     # Match non-bup "ls -a ... /".
130                     parent = resolved[-2] if len(resolved) > 1 else resolved[0]
131                     items = chain(items, (('..', parent[1]),))
132                 for sub_name, sub_item in sorted(items, key=lambda x: x[0]):
133                     if show_hidden != 'all' and sub_name == '.':
134                         continue
135                     if sub_name.startswith('.') and \
136                        show_hidden not in ('almost', 'all'):
137                         continue
138                     if opt.l:
139                         sub_item = vfs.ensure_item_has_metadata(repo, sub_item,
140                                                                 include_size=True)
141                     else:
142                         sub_item = vfs.augment_item_meta(repo, sub_item,
143                                                          include_size=True)
144                     line = item_line(sub_item, sub_name)
145                     pending.append(line) if not opt.l and istty1 else print(line)
146             else:
147                 leaf_item = vfs.augment_item_meta(repo, leaf_item,
148                                                   include_size=True)
149                 line = item_line(leaf_item, os.path.normpath(path))
150                 pending.append(line) if not opt.l and istty1 else print(line)
151         except vfs.IOError as ex:
152             log('bup: %s\n' % ex)
153             ret = 1
154
155     if pending:
156         sys.stdout.write(columnate(pending, ''))
157
158     return ret