2 from support
import PipeThroughCommand
, escape
, Tmp
7 def pipe_through_command(command
, src
, dst
):
9 assert not current_command
10 current_command
= PipeThroughCommand(command
, src
, dst
)
12 current_command
.wait()
14 current_command
= None
20 def __init__(self
, extension
):
21 operations
.append(self
)
22 self
.extension
= extension
24 def can_handle(self
, data
):
25 return isinstance(data
, FileData
)
27 def save_to_stream(self
, data
, stream
):
28 pipe_through_command(self
.command
, data
.source
, stream
)
30 class Compress(Operation
):
31 "Compress a stream into another stream."
34 def __init__(self
, extension
, command
, type):
35 Operation
.__init
__(self
, extension
)
36 self
.command
= command
40 return 'Compress as .%s' % self
.extension
42 class Decompress(Operation
):
43 "Decompress a stream into another stream."
46 def __init__(self
, extension
, command
):
47 Operation
.__init
__(self
, extension
)
48 self
.command
= command
51 return 'Decompress .%s' % self
.extension
53 class Extract(Operation
):
54 "Extract an archive to a directory."
55 type = 'inode/directory'
57 def __init__(self
, extension
, command
):
58 "If command has a %s then the source path is inserted, else uses stdin."
59 Operation
.__init
__(self
, extension
)
60 self
.command
= command
63 return 'Extract from a .%s' % self
.extension
65 def save_to_stream(self
, data
, stream
):
66 raise Exception('This operation creates a directory, so you have '
67 'to drag to a filer window on the local machine')
69 def save_to_file(self
, data
, path
):
70 if os
.path
.exists(path
):
71 if not os
.path
.isdir(path
):
72 raise Exception("'%s' already exists and is not a directory!" %
74 if not os
.path
.exists(path
):
77 command
= self
.command
79 if command
.find("'%s'") != -1:
80 command
= command
% escape(source
.name
)
83 pipe_through_command(command
, source
, None)
86 os
.rmdir(path
) # Will only succeed if it's empty
89 # If we created only a single subdirectory, move it up.
90 dirs
= os
.listdir(path
)
94 unneeded_path
= os
.path
.join(path
, dir)
95 if not os
.path
.isdir(unneeded_path
):
98 tmp_path
= os
.path
.join(path
, 'tmp-' + `random
.randint(0, 100000)`
)
99 os
.rename(unneeded_path
, tmp_path
)
100 for file in os
.listdir(tmp_path
):
101 os
.rename(os
.path
.join(tmp_path
, file), os
.path
.join(path
, file))
104 class Archive(Operation
):
105 "Create an archive from a directory."
108 def __init__(self
, extension
, command
, type):
109 assert command
.find("'%s'") != -1
111 Operation
.__init
__(self
, extension
)
112 self
.command
= command
116 return 'Create .%s archive' % self
.extension
118 def can_handle(self
, data
):
119 return isinstance(data
, DirData
)
121 def save_to_stream(self
, data
, stream
):
122 os
.chdir(os
.path
.dirname(data
.path
))
123 command
= self
.command
% escape(os
.path
.basename(data
.path
))
124 pipe_through_command(command
, None, stream
)
126 tgz
= Extract('tgz', "gunzip -c - | tar xf -")
127 tbz
= Extract('tar.bz2', "bunzip2 -c - | tar xf -")
128 rar
= Extract('rar', "rar x -")
129 tar
= Extract('tar', "tar xf -")
130 rpm
= Extract('rpm', "rpm2cpio - | cpio -id --quiet")
131 cpio
= Extract('cpio', "cpio -id --quiet")
132 deb
= Extract('deb', "ar x '%s'")
133 zip = Extract('zip', "unzip -q '%s'")
134 jar
= Extract('jar', "unzip -q '%s'")
136 make_tgz
= Archive('tgz', "tar cf - '%s' | gzip", 'application/x-compressed-tar')
137 Archive('tar.gz', "tar cf - '%s' | gzip", 'application/x-compressed-tar')
138 Archive('tar.bz2', "tar cf - '%s' | bzip2", 'application/x-bzip-compressed-tar')
139 Archive('zip', "zip -qr - '%s'", 'application/zip'),
140 Archive('jar', "zip -qr - '%s'", 'application/x-jar')
141 Archive('tar', "tar cf - '%s'", 'application/x-tar')
143 # Note: these go afterwards so that .tar.gz matches before .gz
144 make_gz
= Compress('gz', "gzip -c -", 'application/x-gzip')
145 Compress('bz2', "bzip2 -c -", 'application/x-bzip')
147 gz
= Decompress('gz', "gunzip -c -")
148 bz2
= Decompress('bz2', "bunzip2 -ck -")
151 # Can bzip2 read bzip files?
159 known_extensions
= {}
162 known_extensions
[x
.extension
] = None
163 except AttributeError:
167 "A file on the local filesystem."
169 def __init__(self
, path
):
177 self
.mode
= os
.stat(path
).st_mode
179 rox
.report_exception()
183 start
= source
.read(300)
188 # Input is not a regular, local, seekable file, so copy it
189 # to a local temp file.
194 shutil
.copyfileobj(source
, tmp
)
197 self
.default
= self
.guess_format(start
)
203 for ext
in known_extensions
:
204 if path
.endswith('.' + ext
):
205 new
= path
[:-len(ext
)-1]
206 if len(new
) < len(name
):
208 if self
.default
.add_extension
:
209 name
+= '.' + self
.default
.extension
210 self
.default_name
= name
212 def guess_format(self
, data
):
213 "Return a good default Operation, judging by the first 300 bytes or so."
215 def string(offset
, match
):
216 return data
[offset
:offset
+ len(match
)] == match
217 def short(offset
, match
):
221 return ((a
== match
& 0xff) and (b
== (match
>> 8))) or \
222 (b
== match
& 0xff) and (a
== (match
>> 8))
226 if string(257, 'ustar\0') or string(257, 'ustar\040\040\0'):
228 if short(0, 070707) or short(0, 0143561) or string(0, '070707') or \
229 string(0, '070701') or string(0, '070702'):
231 if string(0, '!<arch>') or string(0, '\\<ar>') or string(0, '<ar>'):
232 if string(7, '\ndebian'):
234 if string(0, 'Rar!'): return rar
235 if string(0, 'PK\003\004'): return zip
236 if string(0, '\xed\xab\xee\xdb'): return rpm
239 if string(0, '\037\213'):
240 if self
.path
.endswith('.tar.gz') or self
.path
.endswith('.tgz'):
243 if string(0, 'BZh') or string(0, 'BZ'):
244 if self
.path
.endswith('.tar.bz') or self
.path
.endswith('.tar.bz2') or \
245 self
.path
.endswith('.tbz') or self
.path
.endswith('.tbz2'):
253 def __init__(self
, path
):
255 self
.default
= make_tgz
256 self
.default_name
= path
+ '.' + self
.default
.extension