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