]> arthur.barton.de Git - bup.git/blob - lib/bup/ls.py
Port ls to vfs2
[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
13 def item_info(item, name,
14               show_hash = False,
15               long_fmt = False,
16               classification = None,
17               numeric_ids = False,
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.
21
22     """
23     result = ''
24     if show_hash and hasattr(item, 'oid'):
25         result += '%s ' % item.oid.encode('hex')
26     if long_fmt:
27         meta = item.meta.copy()
28         meta.path = name
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)
34     else:
35         result += name
36         if classification:
37             result += xstat.classification_str(item.meta.mode,
38                                                classification == 'all')
39     return result
40
41
42 optspec = """
43 %sls [-a] [path...]
44 --
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
54 """
55
56 def do_ls(args, default='.', onabort=None, spec_prefix=''):
57     """Output a listing of a file or directory in the bup repository.
58
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.
63
64     """
65     if onabort:
66         o = options.Options(optspec % spec_prefix, onabort=onabort)
67     else:
68         o = options.Options(optspec % spec_prefix)
69     (opt, flags, extra) = o.parse(args)
70
71     # Handle order-sensitive options.
72     classification = None
73     show_hidden = None
74     for flag in flags:
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'):
81             show_hidden = 'all'
82         elif option in ('-A', '--almost-all'):
83             show_hidden = 'almost'
84
85     def item_line(item, name):
86         return item_info(item, name,
87                          show_hash = opt.hash,
88                          long_fmt = opt.l,
89                          classification = classification,
90                          numeric_ids = opt.numeric_ids,
91                          human_readable = opt.human_readable)
92
93     repo = LocalRepo()
94     ret = 0
95     pending = []
96     for path in (extra or [default]):
97         try:
98             if opt.directory:
99                 resolved = vfs.lresolve(repo, path)
100             else:
101                 # FIXME: deal with invalid symlinks i.e. old vfs try_resolve
102                 resolved = vfs.resolve(repo, path)
103
104             leaf_name, leaf_item = resolved[-1]
105             if not leaf_item:
106                 log('error: cannot access %r in %r\n'
107                     % ('/'.join(name for name, item in resolved),
108                        path))
109                 ret = 1
110                 continue
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]),))
117
118                 items = ((x[0], vfs.augment_item_meta(repo, x[1],
119                                                       include_size=True))
120                          for x in items)
121                 for sub_name, sub_item in sorted(items, key=lambda x: x[0]):
122                     if show_hidden != 'all' and sub_name == '.':
123                         continue
124                     if sub_name.startswith('.') and \
125                        show_hidden not in ('almost', 'all'):
126                         continue
127                     line = item_line(sub_item, sub_name)
128                     pending.append(line) if not opt.l and istty1 else print(line)
129             else:
130                 leaf_item = vfs.augment_item_meta(repo, leaf_item,
131                                                   include_size=True)
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)
136             ret = 1
137
138     if pending:
139         sys.stdout.write(columnate(pending, ''))
140
141     return ret