]> arthur.barton.de Git - bup.git/blob - cmd/ftp-cmd.py
f1e48bdbe0a7a3fb11058ce25f7b6eede4e48574
[bup.git] / cmd / ftp-cmd.py
1 #!/bin/sh
2 """": # -*-python-*-
3 bup_python="$(dirname "$0")/bup-python" || exit $?
4 exec "$bup_python" "$0" ${1+"$@"}
5 """
6 # end of bup preamble
7
8 from __future__ import absolute_import, print_function
9 import sys, os, stat, fnmatch
10
11 from bup import options, git, shquote, ls, vfs
12 from bup.io import byte_stream
13 from bup.helpers import chunkyreader, handle_ctrl_c, log
14 from bup.repo import LocalRepo
15
16 handle_ctrl_c()
17
18
19 class OptionError(Exception):
20     pass
21
22
23 def do_ls(repo, args, out):
24     try:
25         opt = ls.opts_from_cmdline(args, onabort=OptionError)
26     except OptionError as e:
27         log('error: %s' % e)
28         return
29     return ls.within_repo(repo, opt, out)
30
31
32 def write_to_file(inf, outf):
33     for blob in chunkyreader(inf):
34         outf.write(blob)
35
36
37 def inputiter():
38     if os.isatty(sys.stdin.fileno()):
39         while 1:
40             try:
41                 yield raw_input('bup> ')
42             except EOFError:
43                 print()  # Clear the line for the terminal's next prompt
44                 break
45     else:
46         for line in sys.stdin:
47             yield line
48
49
50 def _completer_get_subs(repo, line):
51     (qtype, lastword) = shquote.unfinished_word(line)
52     (dir,name) = os.path.split(lastword)
53     dir_path = vfs.resolve(repo, dir or '/')
54     _, dir_item = dir_path[-1]
55     if not dir_item:
56         subs = tuple()
57     else:
58         subs = tuple(dir_path + (entry,)
59                      for entry in vfs.contents(repo, dir_item)
60                      if (entry[0] != '.' and entry[0].startswith(name)))
61     return dir, name, qtype, lastword, subs
62
63
64 _last_line = None
65 _last_res = None
66 def completer(text, iteration):
67     global repo
68     global _last_line
69     global _last_res
70     try:
71         line = readline.get_line_buffer()[:readline.get_endidx()]
72         if _last_line != line:
73             _last_res = _completer_get_subs(repo, line)
74             _last_line = line
75         (dir, name, qtype, lastword, subs) = _last_res
76         if iteration < len(subs):
77             path = subs[iteration]
78             leaf_name, leaf_item = path[-1]
79             res = vfs.try_resolve(repo, leaf_name, parent=path[:-1])
80             leaf_name, leaf_item = res[-1]
81             fullname = os.path.join(*(name for name, item in res))
82             if stat.S_ISDIR(vfs.item_mode(leaf_item)):
83                 ret = shquote.what_to_add(qtype, lastword, fullname+'/',
84                                           terminate=False)
85             else:
86                 ret = shquote.what_to_add(qtype, lastword, fullname,
87                                           terminate=True) + ' '
88             return text + ret
89     except Exception as e:
90         log('\n')
91         try:
92             import traceback
93             traceback.print_tb(sys.exc_traceback)
94         except Exception as e2:
95             log('Error printing traceback: %s\n' % e2)
96         log('\nError in completion: %s\n' % e)
97
98
99 optspec = """
100 bup ftp [commands...]
101 """
102 o = options.Options(optspec)
103 (opt, flags, extra) = o.parse(sys.argv[1:])
104
105 git.check_repo_or_die()
106
107 sys.stdout.flush()
108 out = byte_stream(sys.stdout)
109 repo = LocalRepo()
110 pwd = vfs.resolve(repo, '/')
111 rv = 0
112
113 if extra:
114     lines = extra
115 else:
116     try:
117         import readline
118     except ImportError:
119         log('* readline module not available: line editing disabled.\n')
120         readline = None
121
122     if readline:
123         readline.set_completer_delims(' \t\n\r/')
124         readline.set_completer(completer)
125         if sys.platform.startswith('darwin'):
126             # MacOS uses a slightly incompatible clone of libreadline
127             readline.parse_and_bind('bind ^I rl_complete')
128         readline.parse_and_bind('tab: complete')
129     lines = inputiter()
130
131 for line in lines:
132     if not line.strip():
133         continue
134     words = [word for (wordstart,word) in shquote.quotesplit(line)]
135     cmd = words[0].lower()
136     #log('execute: %r %r\n' % (cmd, parm))
137     try:
138         if cmd == 'ls':
139             # FIXME: respect pwd (perhaps via ls accepting resolve path/parent)
140             sys.stdout.flush()  # FIXME: remove when we finish py3 support
141             do_ls(repo, words[1:], out)
142         elif cmd == 'cd':
143             np = pwd
144             for parm in words[1:]:
145                 res = vfs.resolve(repo, parm, parent=np)
146                 _, leaf_item = res[-1]
147                 if not leaf_item:
148                     raise Exception('%r does not exist'
149                                     % '/'.join(name for name, item in res))
150                 if not stat.S_ISDIR(vfs.item_mode(leaf_item)):
151                     raise Exception('%r is not a directory' % parm)
152                 np = res
153             pwd = np
154         elif cmd == 'pwd':
155             if len(pwd) == 1:
156                 sys.stdout.write('/')
157             print('/'.join(name for name, item in pwd))
158         elif cmd == 'cat':
159             for parm in words[1:]:
160                 res = vfs.resolve(repo, parm, parent=pwd)
161                 _, leaf_item = res[-1]
162                 if not leaf_item:
163                     raise Exception('%r does not exist' %
164                                     '/'.join(name for name, item in res))
165                 with vfs.fopen(repo, leaf_item) as srcfile:
166                     write_to_file(srcfile, sys.stdout)
167         elif cmd == 'get':
168             if len(words) not in [2,3]:
169                 rv = 1
170                 raise Exception('Usage: get <filename> [localname]')
171             rname = words[1]
172             (dir,base) = os.path.split(rname)
173             lname = len(words)>2 and words[2] or base
174             res = vfs.resolve(repo, rname, parent=pwd)
175             _, leaf_item = res[-1]
176             if not leaf_item:
177                 raise Exception('%r does not exist' %
178                                 '/'.join(name for name, item in res))
179             with vfs.fopen(repo, leaf_item) as srcfile:
180                 with open(lname, 'wb') as destfile:
181                     log('Saving %r\n' % lname)
182                     write_to_file(srcfile, destfile)
183         elif cmd == 'mget':
184             for parm in words[1:]:
185                 (dir,base) = os.path.split(parm)
186
187                 res = vfs.resolve(repo, dir, parent=pwd)
188                 _, dir_item = res[-1]
189                 if not dir_item:
190                     raise Exception('%r does not exist' % dir)
191                 for name, item in vfs.contents(repo, dir_item):
192                     if name == '.':
193                         continue
194                     if fnmatch.fnmatch(name, base):
195                         if stat.S_ISLNK(vfs.item_mode(item)):
196                             deref = vfs.resolve(repo, name, parent=res)
197                             deref_name, deref_item = deref[-1]
198                             if not deref_item:
199                                 raise Exception('%r does not exist' %
200                                                 '/'.join(name for name, item
201                                                          in deref))
202                             item = deref_item
203                         with vfs.fopen(repo, item) as srcfile:
204                             with open(name, 'wb') as destfile:
205                                 log('Saving %r\n' % name)
206                                 write_to_file(srcfile, destfile)
207         elif cmd == 'help' or cmd == '?':
208             # FIXME: move to stdout
209             log('Commands: ls cd pwd cat get mget help quit\n')
210         elif cmd in ('quit', 'exit', 'bye'):
211             break
212         else:
213             rv = 1
214             raise Exception('no such command %r' % cmd)
215     except Exception as e:
216         rv = 1
217         log('error: %s\n' % e)
218         raise
219
220 sys.exit(rv)