]> arthur.barton.de Git - bup.git/blob - lib/bup/ls.py
vfs2: add try_resolve() and use it in ls
[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(item.meta.mode,
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
133                 items = ((x[0], vfs.augment_item_meta(repo, x[1],
134                                                       include_size=True))
135                          for x in items)
136                 for sub_name, sub_item in sorted(items, key=lambda x: x[0]):
137                     if show_hidden != 'all' and sub_name == '.':
138                         continue
139                     if sub_name.startswith('.') and \
140                        show_hidden not in ('almost', 'all'):
141                         continue
142                     line = item_line(sub_item, sub_name)
143                     pending.append(line) if not opt.l and istty1 else print(line)
144             else:
145                 leaf_item = vfs.augment_item_meta(repo, leaf_item,
146                                                   include_size=True)
147                 line = item_line(leaf_item, os.path.normpath(path))
148                 pending.append(line) if not opt.l and istty1 else print(line)
149         except vfs.IOError as ex:
150             log('bup: %s\n' % ex)
151             ret = 1
152
153     if pending:
154         sys.stdout.write(columnate(pending, ''))
155
156     return ret