]> arthur.barton.de Git - bup.git/blob - lib/bup/cmd/meta.py
Remove Client __del__ in favor of context management
[bup.git] / lib / bup / cmd / meta.py
1 # Copyright (C) 2010 Rob Browning
2 #
3 # This code is covered under the terms of the GNU Library General
4 # Public License as described in the bup LICENSE file.
5
6 # TODO: Add tar-like -C option.
7
8 from __future__ import absolute_import
9 import sys
10
11 from bup import metadata
12 from bup import options
13 from bup.compat import argv_bytes
14 from bup.io import byte_stream
15 from bup.helpers import log, saved_errors
16
17
18 def open_input(name):
19     if not name or name == b'-':
20         return byte_stream(sys.stdin)
21     return open(name, 'rb')
22
23
24 def open_output(name):
25     if not name or name == b'-':
26         sys.stdout.flush()
27         return byte_stream(sys.stdout)
28     return open(name, 'wb')
29
30
31 optspec = """
32 bup meta --create [OPTION ...] <PATH ...>
33 bup meta --list [OPTION ...]
34 bup meta --extract [OPTION ...]
35 bup meta --start-extract [OPTION ...]
36 bup meta --finish-extract [OPTION ...]
37 bup meta --edit [OPTION ...] <PATH ...>
38 --
39 c,create       write metadata for PATHs to stdout (or --file)
40 t,list         display metadata
41 x,extract      perform --start-extract followed by --finish-extract
42 start-extract  build tree matching metadata provided on standard input (or --file)
43 finish-extract finish applying standard input (or --file) metadata to filesystem
44 edit           alter metadata; write to stdout (or --file)
45 f,file=        specify source or destination file
46 R,recurse      recurse into subdirectories
47 xdev,one-file-system  don't cross filesystem boundaries
48 numeric-ids    apply numeric IDs (user, group, etc.) rather than names
49 symlinks       handle symbolic links (default is true)
50 paths          include paths in metadata (default is true)
51 set-uid=       set metadata uid (via --edit)
52 set-gid=       set metadata gid (via --edit)
53 set-user=      set metadata user (via --edit)
54 unset-user     remove metadata user (via --edit)
55 set-group=     set metadata group (via --edit)
56 unset-group    remove metadata group (via --edit)
57 v,verbose      increase log output (can be used more than once)
58 q,quiet        don't show progress meter
59 """
60
61 def main(argv):
62
63     o = options.Options(optspec)
64     opt, flags, remainder = o.parse_bytes([b'--paths', b'--symlinks', b'--recurse']
65                                           + argv[1:])
66
67     opt.verbose = opt.verbose or 0
68     opt.quiet = opt.quiet or 0
69     metadata.verbose = opt.verbose - opt.quiet
70     opt.file = argv_bytes(opt.file) if opt.file else None
71
72     action_count = sum([bool(x) for x in [opt.create, opt.list, opt.extract,
73                                           opt.start_extract, opt.finish_extract,
74                                           opt.edit]])
75     if action_count > 1:
76         o.fatal("bup: only one action permitted: --create --list --extract --edit")
77     if action_count == 0:
78         o.fatal("bup: no action specified")
79
80     if opt.create:
81         if len(remainder) < 1:
82             o.fatal("no paths specified for create")
83         output_file = open_output(opt.file)
84         metadata.save_tree(output_file,
85                            [argv_bytes(r) for r in remainder],
86                            recurse=opt.recurse,
87                            write_paths=opt.paths,
88                            save_symlinks=opt.symlinks,
89                            xdev=opt.xdev)
90     elif opt.list:
91         if len(remainder) > 0:
92             o.fatal("cannot specify paths for --list")
93         src = open_input(opt.file)
94         metadata.display_archive(src, open_output(b'-'))
95     elif opt.start_extract:
96         if len(remainder) > 0:
97             o.fatal("cannot specify paths for --start-extract")
98         src = open_input(opt.file)
99         metadata.start_extract(src, create_symlinks=opt.symlinks)
100     elif opt.finish_extract:
101         if len(remainder) > 0:
102             o.fatal("cannot specify paths for --finish-extract")
103         src = open_input(opt.file)
104         metadata.finish_extract(src, restore_numeric_ids=opt.numeric_ids)
105     elif opt.extract:
106         if len(remainder) > 0:
107             o.fatal("cannot specify paths for --extract")
108         src = open_input(opt.file)
109         metadata.extract(src,
110                          restore_numeric_ids=opt.numeric_ids,
111                          create_symlinks=opt.symlinks)
112     elif opt.edit:
113         if len(remainder) < 1:
114             o.fatal("no paths specified for edit")
115         output_file = open_output(opt.file)
116
117         unset_user = False # True if --unset-user was the last relevant option.
118         unset_group = False # True if --unset-group was the last relevant option.
119         for flag in flags:
120             if flag[0] == '--set-user':
121                 unset_user = False
122             elif flag[0] == '--unset-user':
123                 unset_user = True
124             elif flag[0] == '--set-group':
125                 unset_group = False
126             elif flag[0] == '--unset-group':
127                 unset_group = True
128
129         for path in remainder:
130             f = open(argv_bytes(path), 'rb')
131             try:
132                 for m in metadata._ArchiveIterator(f):
133                     if opt.set_uid is not None:
134                         try:
135                             m.uid = int(opt.set_uid)
136                         except ValueError:
137                             o.fatal("uid must be an integer")
138
139                     if opt.set_gid is not None:
140                         try:
141                             m.gid = int(opt.set_gid)
142                         except ValueError:
143                             o.fatal("gid must be an integer")
144
145                     if unset_user:
146                         m.user = b''
147                     elif opt.set_user is not None:
148                         m.user = argv_bytes(opt.set_user)
149
150                     if unset_group:
151                         m.group = b''
152                     elif opt.set_group is not None:
153                         m.group = argv_bytes(opt.set_group)
154
155                     m.write(output_file)
156             finally:
157                 f.close()
158
159
160     if saved_errors:
161         log('WARNING: %d errors encountered.\n' % len(saved_errors))
162         sys.exit(1)
163     else:
164         sys.exit(0)