3 bup_python="$(dirname "$0")/bup-python" || exit $?
4 exec "$bup_python" "$0" ${1+"$@"}
8 # For now, this completely relies on the assumption that the current
9 # encoding (LC_CTYPE, etc.) is ASCII compatible, and that it returns
10 # the exact same bytes from a decode/encode round-trip (or the reverse
13 from __future__ import absolute_import, print_function
14 import sys, os, stat, fnmatch
16 from bup import _helpers, options, git, shquote, ls, vfs
17 from bup.compat import argv_bytes
18 from bup.helpers import chunkyreader, handle_ctrl_c, log
19 from bup.io import byte_stream, path_msg
20 from bup.repo import LocalRepo
25 class OptionError(Exception):
29 def do_ls(repo, args, out):
31 opt = ls.opts_from_cmdline(args, onabort=OptionError)
32 except OptionError as e:
35 return ls.within_repo(repo, opt, out)
38 def write_to_file(inf, outf):
39 for blob in chunkyreader(inf):
44 if os.isatty(stdin.fileno()):
47 yield _helpers.readline(b'bup> ')
49 print() # Clear the line for the terminal's next prompt
56 def _completer_get_subs(repo, line):
57 (qtype, lastword) = shquote.unfinished_word(line)
58 dir, name = os.path.split(lastword)
59 dir_path = vfs.resolve(repo, dir or b'/')
60 _, dir_item = dir_path[-1]
64 subs = tuple(dir_path + (entry,)
65 for entry in vfs.contents(repo, dir_item)
66 if (entry[0] != b'.' and entry[0].startswith(name)))
67 return qtype, lastword, subs
72 def attempt_completion(text, start, end):
73 global _attempt_start, _attempt_end
74 _attempt_start = start
80 def enter_completion(text, iteration):
86 line = _helpers.get_line_buffer()[:_attempt_end]
87 if _last_line != line:
88 _last_res = _completer_get_subs(repo, line)
90 qtype, lastword, subs = _last_res
91 if iteration < len(subs):
92 path = subs[iteration]
93 leaf_name, leaf_item = path[-1]
94 res = vfs.try_resolve(repo, leaf_name, parent=path[:-1])
95 leaf_name, leaf_item = res[-1]
96 fullname = os.path.join(*(name for name, item in res))
97 if stat.S_ISDIR(vfs.item_mode(leaf_item)):
98 ret = shquote.what_to_add(qtype, lastword, fullname + b'/',
101 ret = shquote.what_to_add(qtype, lastword, fullname,
102 terminate=True) + b' '
104 except Exception as e:
108 traceback.print_tb(sys.exc_traceback)
109 except Exception as e2:
110 log('Error printing traceback: %s\n' % e2)
111 log('\nError in completion: %s\n' % e)
115 bup ftp [commands...]
117 o = options.Options(optspec)
118 (opt, flags, extra) = o.parse(sys.argv[1:])
120 git.check_repo_or_die()
123 out = byte_stream(sys.stdout)
124 stdin = byte_stream(sys.stdin)
126 pwd = vfs.resolve(repo, b'/')
132 lines = (argv_bytes(arg) for arg in extra)
134 if hasattr(_helpers, 'readline'):
135 _helpers.set_completer_word_break_characters(b' \t\n\r/')
136 _helpers.set_attempted_completion_function(attempt_completion)
137 _helpers.set_completion_entry_function(enter_completion)
138 if sys.platform.startswith('darwin'):
139 # MacOS uses a slightly incompatible clone of libreadline
140 _helpers.parse_and_bind(b'bind ^I rl_complete')
141 _helpers.parse_and_bind(b'tab: complete')
147 words = [word for (wordstart,word) in shquote.quotesplit(line)]
148 cmd = words[0].lower()
149 #log('execute: %r %r\n' % (cmd, parm))
152 # FIXME: respect pwd (perhaps via ls accepting resolve path/parent)
153 do_ls(repo, words[1:], out)
157 for parm in words[1:]:
158 res = vfs.resolve(repo, parm, parent=np)
159 _, leaf_item = res[-1]
161 raise Exception('%s does not exist'
162 % path_msg(b'/'.join(name for name, item
164 if not stat.S_ISDIR(vfs.item_mode(leaf_item)):
165 raise Exception('%s is not a directory' % path_msg(parm))
171 out.write(b'/'.join(name for name, item in pwd) + b'\n')
174 for parm in words[1:]:
175 res = vfs.resolve(repo, parm, parent=pwd)
176 _, leaf_item = res[-1]
178 raise Exception('%s does not exist' %
179 path_msg(b'/'.join(name for name, item
181 with vfs.fopen(repo, leaf_item) as srcfile:
182 write_to_file(srcfile, out)
185 if len(words) not in [2,3]:
187 raise Exception('Usage: get <filename> [localname]')
189 (dir,base) = os.path.split(rname)
190 lname = len(words) > 2 and words[2] or base
191 res = vfs.resolve(repo, rname, parent=pwd)
192 _, leaf_item = res[-1]
194 raise Exception('%s does not exist' %
195 path_msg(b'/'.join(name for name, item in res)))
196 with vfs.fopen(repo, leaf_item) as srcfile:
197 with open(lname, 'wb') as destfile:
198 log('Saving %s\n' % path_msg(lname))
199 write_to_file(srcfile, destfile)
201 for parm in words[1:]:
202 dir, base = os.path.split(parm)
204 res = vfs.resolve(repo, dir, parent=pwd)
205 _, dir_item = res[-1]
207 raise Exception('%s does not exist' % path_msg(dir))
208 for name, item in vfs.contents(repo, dir_item):
211 if fnmatch.fnmatch(name, base):
212 if stat.S_ISLNK(vfs.item_mode(item)):
213 deref = vfs.resolve(repo, name, parent=res)
214 deref_name, deref_item = deref[-1]
216 raise Exception('%s does not exist' %
217 path_msg('/'.join(name for name, item
220 with vfs.fopen(repo, item) as srcfile:
221 with open(name, 'wb') as destfile:
222 log('Saving %s\n' % path_msg(name))
223 write_to_file(srcfile, destfile)
224 elif cmd == b'help' or cmd == b'?':
225 out.write(b'Commands: ls cd pwd cat get mget help quit\n')
227 elif cmd in (b'quit', b'exit', b'bye'):
231 raise Exception('no such command %r' % cmd)
232 except Exception as e:
234 log('error: %s\n' % e)