3 # https://sourceware.org/bugzilla/show_bug.cgi?id=26034
4 export "BUP_ARGV_0"="$0"
7 export "BUP_ARGV_${arg_i}"="$arg"
11 # Here to end of preamble replaced during install
12 bup_python="$(dirname "$0")/../../config/bin/python" || exit $?
13 exec "$bup_python" "$0"
17 # For now, this completely relies on the assumption that the current
18 # encoding (LC_CTYPE, etc.) is ASCII compatible, and that it returns
19 # the exact same bytes from a decode/encode round-trip (or the reverse
22 from __future__ import absolute_import, print_function
23 import os, fnmatch, stat, sys
25 sys.path[:0] = [os.path.dirname(os.path.realpath(__file__)) + '/..']
27 from bup import _helpers, compat, options, git, shquote, ls, vfs
28 from bup.compat import argv_bytes, fsdecode
29 from bup.helpers import chunkyreader, handle_ctrl_c, log
30 from bup.io import byte_stream, path_msg
31 from bup.repo import LocalRepo
36 class OptionError(Exception):
40 def do_ls(repo, args, out):
42 opt = ls.opts_from_cmdline([fsdecode(arg) for arg in args],
44 except OptionError as e:
47 return ls.within_repo(repo, opt, out)
50 def write_to_file(inf, outf):
51 for blob in chunkyreader(inf):
56 if os.isatty(stdin.fileno()):
58 if hasattr(_helpers, 'readline'):
60 yield _helpers.readline(b'bup> ')
62 print() # Clear the line for the terminal's next prompt
67 read_line = stdin.readline()
77 def _completer_get_subs(repo, line):
78 (qtype, lastword) = shquote.unfinished_word(line)
79 dir, name = os.path.split(lastword)
80 dir_path = vfs.resolve(repo, dir or b'/')
81 _, dir_item = dir_path[-1]
85 subs = tuple(dir_path + (entry,)
86 for entry in vfs.contents(repo, dir_item)
87 if (entry[0] != b'.' and entry[0].startswith(name)))
88 return qtype, lastword, subs
93 def attempt_completion(text, start, end):
94 global _attempt_start, _attempt_end
95 _attempt_start = start
101 def enter_completion(text, iteration):
107 line = _helpers.get_line_buffer()[:_attempt_end]
108 if _last_line != line:
109 _last_res = _completer_get_subs(repo, line)
111 qtype, lastword, subs = _last_res
112 if iteration < len(subs):
113 path = subs[iteration]
114 leaf_name, leaf_item = path[-1]
115 res = vfs.try_resolve(repo, leaf_name, parent=path[:-1])
116 leaf_name, leaf_item = res[-1]
117 fullname = os.path.join(*(name for name, item in res))
118 if stat.S_ISDIR(vfs.item_mode(leaf_item)):
119 ret = shquote.what_to_add(qtype, lastword, fullname + b'/',
122 ret = shquote.what_to_add(qtype, lastword, fullname,
123 terminate=True) + b' '
125 except Exception as e:
129 traceback.print_tb(sys.exc_traceback)
130 except Exception as e2:
131 log('Error printing traceback: %s\n' % e2)
132 log('\nError in completion: %s\n' % e)
136 bup ftp [commands...]
138 o = options.Options(optspec)
139 opt, flags, extra = o.parse(compat.argv[1:])
141 git.check_repo_or_die()
144 out = byte_stream(sys.stdout)
145 stdin = byte_stream(sys.stdin)
147 pwd = vfs.resolve(repo, b'/')
153 lines = (argv_bytes(arg) for arg in extra)
155 if hasattr(_helpers, 'readline'):
156 _helpers.set_completer_word_break_characters(b' \t\n\r/')
157 _helpers.set_attempted_completion_function(attempt_completion)
158 _helpers.set_completion_entry_function(enter_completion)
159 if sys.platform.startswith('darwin'):
160 # MacOS uses a slightly incompatible clone of libreadline
161 _helpers.parse_and_bind(b'bind ^I rl_complete')
162 _helpers.parse_and_bind(b'tab: complete')
168 words = [word for (wordstart,word) in shquote.quotesplit(line)]
169 cmd = words[0].lower()
170 #log('execute: %r %r\n' % (cmd, parm))
173 # FIXME: respect pwd (perhaps via ls accepting resolve path/parent)
174 do_ls(repo, words[1:], out)
178 for parm in words[1:]:
179 res = vfs.resolve(repo, parm, parent=np)
180 _, leaf_item = res[-1]
182 raise Exception('%s does not exist'
183 % path_msg(b'/'.join(name for name, item
185 if not stat.S_ISDIR(vfs.item_mode(leaf_item)):
186 raise Exception('%s is not a directory' % path_msg(parm))
192 out.write(b'/'.join(name for name, item in pwd) + b'\n')
195 for parm in words[1:]:
196 res = vfs.resolve(repo, parm, parent=pwd)
197 _, leaf_item = res[-1]
199 raise Exception('%s does not exist' %
200 path_msg(b'/'.join(name for name, item
202 with vfs.fopen(repo, leaf_item) as srcfile:
203 write_to_file(srcfile, out)
206 if len(words) not in [2,3]:
208 raise Exception('Usage: get <filename> [localname]')
210 (dir,base) = os.path.split(rname)
211 lname = len(words) > 2 and words[2] or base
212 res = vfs.resolve(repo, rname, parent=pwd)
213 _, leaf_item = res[-1]
215 raise Exception('%s does not exist' %
216 path_msg(b'/'.join(name for name, item in res)))
217 with vfs.fopen(repo, leaf_item) as srcfile:
218 with open(lname, 'wb') as destfile:
219 log('Saving %s\n' % path_msg(lname))
220 write_to_file(srcfile, destfile)
222 for parm in words[1:]:
223 dir, base = os.path.split(parm)
225 res = vfs.resolve(repo, dir, parent=pwd)
226 _, dir_item = res[-1]
228 raise Exception('%s does not exist' % path_msg(dir))
229 for name, item in vfs.contents(repo, dir_item):
232 if fnmatch.fnmatch(name, base):
233 if stat.S_ISLNK(vfs.item_mode(item)):
234 deref = vfs.resolve(repo, name, parent=res)
235 deref_name, deref_item = deref[-1]
237 raise Exception('%s does not exist' %
238 path_msg('/'.join(name for name, item
241 with vfs.fopen(repo, item) as srcfile:
242 with open(name, 'wb') as destfile:
243 log('Saving %s\n' % path_msg(name))
244 write_to_file(srcfile, destfile)
245 elif cmd == b'help' or cmd == b'?':
246 out.write(b'Commands: ls cd pwd cat get mget help quit\n')
248 elif cmd in (b'quit', b'exit', b'bye'):
252 raise Exception('no such command %r' % cmd)
253 except Exception as e:
255 log('error: %s\n' % e)