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