]> arthur.barton.de Git - bup.git/blob - cmd/ftp-cmd.py
ee2bff1218138d2539b10fb7996c32d8569f04c6
[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 import sys, os, stat, fnmatch
9
10 from bup import options, git, shquote, ls, vfs
11 from bup.helpers import chunkyreader, handle_ctrl_c, log
12 from bup.repo import LocalRepo
13
14 handle_ctrl_c()
15
16
17 class OptionError(Exception):
18     pass
19
20
21 def do_ls(repo, args):
22     try:
23         opt = ls.opts_from_cmdline(args, onabort=OptionError)
24     except OptionError as e:
25         log('error: %s' % e)
26         return
27     return ls.within_repo(repo, opt)
28
29
30 def write_to_file(inf, outf):
31     for blob in chunkyreader(inf):
32         outf.write(blob)
33
34
35 def inputiter():
36     if os.isatty(sys.stdin.fileno()):
37         while 1:
38             try:
39                 yield raw_input('bup> ')
40             except EOFError:
41                 print ''  # Clear the line for the terminal's next prompt
42                 break
43     else:
44         for line in sys.stdin:
45             yield line
46
47
48 def _completer_get_subs(repo, line):
49     (qtype, lastword) = shquote.unfinished_word(line)
50     (dir,name) = os.path.split(lastword)
51     dir_path = vfs.resolve(repo, dir or '/')
52     _, dir_item = dir_path[-1]
53     if not dir_item:
54         subs = tuple()
55     else:
56         subs = tuple(dir_path + (entry,)
57                      for entry in vfs.contents(repo, dir_item)
58                      if (entry[0] != '.' and entry[0].startswith(name)))
59     return dir, name, qtype, lastword, subs
60
61
62 def find_readline_lib():
63     """Return the name (and possibly the full path) of the readline library
64     linked to the given readline module.
65     """
66     import readline
67     f = open(readline.__file__, "rb")
68     try:
69         data = f.read()
70     finally:
71         f.close()
72     import re
73     m = re.search('\0([^\0]*libreadline[^\0]*)\0', data)
74     if m:
75         return m.group(1)
76     return None
77
78
79 def init_readline_vars():
80     """Work around trailing space automatically inserted by readline.
81     See http://bugs.python.org/issue5833"""
82     try:
83         import ctypes
84     except ImportError:
85         # python before 2.5 didn't have the ctypes module; but those
86         # old systems probably also didn't have this readline bug, so
87         # just ignore it.
88         return
89     lib_name = find_readline_lib()
90     if lib_name is not None:
91         lib = ctypes.cdll.LoadLibrary(lib_name)
92         global rl_completion_suppress_append
93         rl_completion_suppress_append = ctypes.c_int.in_dll(lib,
94                                     "rl_completion_suppress_append")
95
96
97 rl_completion_suppress_append = None
98 _last_line = None
99 _last_res = None
100 def completer(text, iteration):
101     global repo
102     global _last_line
103     global _last_res
104     global rl_completion_suppress_append
105     if rl_completion_suppress_append is not None:
106         rl_completion_suppress_append.value = 1
107     try:
108         line = readline.get_line_buffer()[:readline.get_endidx()]
109         if _last_line != line:
110             _last_res = _completer_get_subs(repo, line)
111             _last_line = line
112         (dir, name, qtype, lastword, subs) = _last_res
113         if iteration < len(subs):
114             path = subs[iteration]
115             leaf_name, leaf_item = path[-1]
116             res = vfs.try_resolve(repo, leaf_name, parent=path[:-1])
117             leaf_name, leaf_item = res[-1]
118             fullname = os.path.join(*(name for name, item in res))
119             if stat.S_ISDIR(vfs.item_mode(leaf_item)):
120                 ret = shquote.what_to_add(qtype, lastword, fullname+'/',
121                                           terminate=False)
122             else:
123                 ret = shquote.what_to_add(qtype, lastword, fullname,
124                                           terminate=True) + ' '
125             return text + ret
126     except Exception as e:
127         log('\n')
128         try:
129             import traceback
130             traceback.print_tb(sys.exc_traceback)
131         except Exception as e2:
132             log('Error printing traceback: %s\n' % e2)
133         log('\nError in completion: %s\n' % e)
134
135
136 optspec = """
137 bup ftp [commands...]
138 """
139 o = options.Options(optspec)
140 (opt, flags, extra) = o.parse(sys.argv[1:])
141
142 git.check_repo_or_die()
143
144 repo = LocalRepo()
145 pwd = vfs.resolve(repo, '/')
146 rv = 0
147
148 if extra:
149     lines = extra
150 else:
151     try:
152         import readline
153     except ImportError:
154         log('* readline module not available: line editing disabled.\n')
155         readline = None
156
157     if readline:
158         readline.set_completer_delims(' \t\n\r/')
159         readline.set_completer(completer)
160         if sys.platform.startswith('darwin'):
161             # MacOS uses a slightly incompatible clone of libreadline
162             readline.parse_and_bind('bind ^I rl_complete')
163         readline.parse_and_bind('tab: complete')
164         init_readline_vars()
165     lines = inputiter()
166
167 for line in lines:
168     if not line.strip():
169         continue
170     words = [word for (wordstart,word) in shquote.quotesplit(line)]
171     cmd = words[0].lower()
172     #log('execute: %r %r\n' % (cmd, parm))
173     try:
174         if cmd == 'ls':
175             # FIXME: respect pwd (perhaps via ls accepting resolve path/parent)
176             do_ls(repo, words[1:])
177         elif cmd == 'cd':
178             np = pwd
179             for parm in words[1:]:
180                 res = vfs.resolve(repo, parm, parent=np)
181                 _, leaf_item = res[-1]
182                 if not leaf_item:
183                     raise Exception('%r does not exist'
184                                     % '/'.join(name for name, item in res))
185                 if not stat.S_ISDIR(vfs.item_mode(leaf_item)):
186                     raise Exception('%r is not a directory' % parm)
187                 np = res
188             pwd = np
189         elif cmd == 'pwd':
190             if len(pwd) == 1:
191                 sys.stdout.write('/')
192             print '/'.join(name for name, item in pwd)
193         elif cmd == 'cat':
194             for parm in words[1:]:
195                 res = vfs.resolve(repo, parm, parent=pwd)
196                 _, leaf_item = res[-1]
197                 if not leaf_item:
198                     raise Exception('%r does not exist' %
199                                     '/'.join(name for name, item in res))
200                 with vfs.fopen(repo, leaf_item) as srcfile:
201                     write_to_file(srcfile, sys.stdout)
202         elif cmd == 'get':
203             if len(words) not in [2,3]:
204                 rv = 1
205                 raise Exception('Usage: get <filename> [localname]')
206             rname = words[1]
207             (dir,base) = os.path.split(rname)
208             lname = len(words)>2 and words[2] or base
209             res = vfs.resolve(repo, rname, parent=pwd)
210             _, leaf_item = res[-1]
211             if not leaf_item:
212                 raise Exception('%r does not exist' %
213                                 '/'.join(name for name, item in res))
214             with vfs.fopen(repo, leaf_item) as srcfile:
215                 with open(lname, 'wb') as destfile:
216                     log('Saving %r\n' % lname)
217                     write_to_file(srcfile, destfile)
218         elif cmd == 'mget':
219             for parm in words[1:]:
220                 (dir,base) = os.path.split(parm)
221
222                 res = vfs.resolve(repo, dir, parent=pwd)
223                 _, dir_item = res[-1]
224                 if not dir_item:
225                     raise Exception('%r does not exist' % dir)
226                 for name, item in vfs.contents(repo, dir_item):
227                     if name == '.':
228                         continue
229                     if fnmatch.fnmatch(name, base):
230                         if stat.S_ISLNK(vfs.item_mode(item)):
231                             deref = vfs.resolve(repo, name, parent=res)
232                             deref_name, deref_item = deref[-1]
233                             if not deref_item:
234                                 raise Exception('%r does not exist' %
235                                                 '/'.join(name for name, item
236                                                          in deref))
237                             item = deref_item
238                         with vfs.fopen(repo, item) as srcfile:
239                             with open(name, 'wb') as destfile:
240                                 log('Saving %r\n' % name)
241                                 write_to_file(srcfile, destfile)
242         elif cmd == 'help' or cmd == '?':
243             # FIXME: move to stdout
244             log('Commands: ls cd pwd cat get mget help quit\n')
245         elif cmd in ('quit', 'exit', 'bye'):
246             break
247         else:
248             rv = 1
249             raise Exception('no such command %r' % cmd)
250     except Exception as e:
251         rv = 1
252         log('error: %s\n' % e)
253         raise
254
255 sys.exit(rv)