1 if __name__
== '__main__':
2 import findrox
; findrox
.version(1, 99, 11)
4 from support
import PipeThroughCommand
, escape
, Tmp
9 def pipe_through_command(command
, src
, dst
):
10 global current_command
11 assert not current_command
16 current_command
= PipeThroughCommand(command
, src
, dst
)
18 current_command
.wait()
20 current_command
= None
26 def __init__(self
, extension
):
27 operations
.append(self
)
28 self
.extension
= extension
30 def can_handle(self
, data
):
31 return isinstance(data
, FileData
)
33 def save_to_stream(self
, data
, stream
):
34 pipe_through_command(self
.command
, data
.source
, stream
)
36 class Compress(Operation
):
37 "Compress a stream into another stream."
40 def __init__(self
, extension
, command
, type):
41 Operation
.__init
__(self
, extension
)
42 self
.command
= command
46 return _('Compress as .%s') % self
.extension
48 class Decompress(Operation
):
49 "Decompress a stream into another stream."
52 def __init__(self
, extension
, command
):
53 Operation
.__init
__(self
, extension
)
54 self
.command
= command
57 return _('Decompress .%s') % self
.extension
59 class Extract(Operation
):
60 "Extract an archive to a directory."
61 type = 'inode/directory'
63 def __init__(self
, extension
, command
):
64 "If command has a %s then the source path is inserted, else uses stdin."
65 Operation
.__init
__(self
, extension
)
66 self
.command
= command
69 return _('Extract from a .%s') % self
.extension
71 def save_to_stream(self
, data
, stream
):
72 raise Exception(_('This operation creates a directory, so you have '
73 'to drag to a filer window on the local machine'))
75 def save_to_file(self
, data
, path
):
76 if os
.path
.exists(path
):
77 if not os
.path
.isdir(path
):
78 raise Exception(_("'%s' already exists and is not a directory!") %
80 if not os
.path
.exists(path
):
83 command
= self
.command
85 if command
.find("'%s'") != -1:
86 command
= command
% escape(source
.name
)
89 pipe_through_command(command
, source
, None)
92 os
.rmdir(path
) # Will only succeed if it's empty
95 if os
.path
.exists(path
):
98 def pull_up(self
, path
):
99 # If we created only a single subdirectory, move it up.
100 dirs
= os
.listdir(path
)
104 unneeded_path
= os
.path
.join(path
, dir)
105 if not os
.path
.isdir(unneeded_path
):
108 tmp_path
= os
.path
.join(path
, 'tmp-' + `random
.randint(0, 100000)`
)
109 os
.rename(unneeded_path
, tmp_path
)
110 for file in os
.listdir(tmp_path
):
111 os
.rename(os
.path
.join(tmp_path
, file), os
.path
.join(path
, file))
114 class Archive(Operation
):
115 "Create an archive from a directory."
118 def __init__(self
, extension
, command
, type):
119 assert command
.find("'%s'") != -1
121 Operation
.__init
__(self
, extension
)
122 self
.command
= command
126 return _('Create .%s archive') % self
.extension
128 def can_handle(self
, data
):
129 return isinstance(data
, DirData
)
131 def save_to_stream(self
, data
, stream
):
132 os
.chdir(os
.path
.dirname(data
.path
))
133 command
= self
.command
% escape(os
.path
.basename(data
.path
))
134 pipe_through_command(command
, None, stream
)
136 tgz
= Extract('tgz', "gunzip -c - | tar xf -")
137 tbz
= Extract('tar.bz2', "bunzip2 -c - | tar xf -")
138 tarz
= Extract('tar.Z', "uncompress -c - | tar xf -")
139 rar
= Extract('rar', "rar x '%s'")
140 tar
= Extract('tar', "tar xf -")
141 rpm
= Extract('rpm', "rpm2cpio - | cpio -id --quiet")
142 cpio
= Extract('cpio', "cpio -id --quiet")
143 deb
= Extract('deb', "ar x '%s'")
144 zip = Extract('zip', "unzip -q '%s'")
145 jar
= Extract('jar', "unzip -q '%s'")
147 make_tgz
= Archive('tgz', "tar cf - '%s' | gzip", 'application/x-compressed-tar')
148 Archive('tar.gz', "tar cf - '%s' | gzip", 'application/x-compressed-tar')
149 Archive('tar.bz2', "tar cf - '%s' | bzip2", 'application/x-bzip-compressed-tar')
150 Archive('zip', "zip -qr - '%s'", 'application/zip'),
151 Archive('jar', "zip -qr - '%s'", 'application/x-jar')
152 Archive('tar', "tar cf - '%s'", 'application/x-tar')
154 # Note: these go afterwards so that .tar.gz matches before .gz
155 make_gz
= Compress('gz', "gzip -c -", 'application/x-gzip')
156 Compress('bz2', "bzip2 -c -", 'application/x-bzip')
157 Compress('uue', "uuencode /dev/stdout", 'application/x-uuencoded')
159 gz
= Decompress('gz', "gunzip -c -")
160 bz2
= Decompress('bz2', "bunzip2 -ck -")
161 uue
= Decompress('uue', "uudecode -o /dev/stdout")
162 z
= Decompress('Z', "uncompress -c -")
165 # Can bzip2 read bzip files?
174 known_extensions
= {}
177 known_extensions
[x
.extension
] = None
178 except AttributeError:
182 "A file on the local filesystem."
184 def __init__(self
, path
):
192 self
.mode
= os
.stat(path
).st_mode
194 rox
.report_exception()
198 start
= source
.read(300)
200 if source
is sys
.stdin
:
201 raise Exception("Always copy stdin!")
205 # Input is not a regular, local, seekable file, so copy it
206 # to a local temp file.
211 shutil
.copyfileobj(source
, tmp
)
215 self
.default
= self
.guess_format(start
)
221 for ext
in known_extensions
:
222 if path
.endswith('.' + ext
):
223 new
= path
[:-len(ext
)-1]
224 if len(new
) < len(name
):
226 if self
.default
.add_extension
:
227 name
+= '.' + self
.default
.extension
228 self
.default_name
= name
230 def guess_format(self
, data
):
231 "Return a good default Operation, judging by the first 300 bytes or so."
233 def string(offset
, match
):
234 return data
[offset
:offset
+ len(match
)] == match
235 def short(offset
, match
):
239 return ((a
== match
& 0xff) and (b
== (match
>> 8))) or \
240 (b
== match
& 0xff) and (a
== (match
>> 8))
244 if string(257, 'ustar\0') or string(257, 'ustar\040\040\0'):
246 if short(0, 070707) or short(0, 0143561) or string(0, '070707') or \
247 string(0, '070701') or string(0, '070702'):
249 if string(0, '!<arch>') or string(0, '\\<ar>') or string(0, '<ar>'):
250 if string(7, '\ndebian'):
252 if string(0, 'Rar!'): return rar
253 if string(0, 'PK\003\004'): return zip
254 if string(0, 'PK00'): return zip
255 if string(0, '\xed\xab\xee\xdb'): return rpm
258 if string(0, '\037\213'):
259 if self
.path
.endswith('.tar.gz') or self
.path
.endswith('.tgz'):
262 if string(0, 'BZh') or string(0, 'BZ'):
263 if self
.path
.endswith('.tar.bz') or self
.path
.endswith('.tar.bz2') or \
264 self
.path
.endswith('.tbz') or self
.path
.endswith('.tbz2'):
267 if string(0, 'begin '):
269 if string(0, '\037\235'):
270 if self
.path
.endswith('.tar.Z'):
278 def __init__(self
, path
):
280 self
.default
= make_tgz
281 self
.default_name
= path
+ '.' + self
.default
.extension
284 test_data
= 'Hello\0World\n'
288 data
= FileData(src
.name
)
289 for comp
in operations
:
290 if not isinstance(comp
, Compress
): continue
291 dec
= [o
for o
in operations
if isinstance(o
, Decompress
) and
292 o
.extension
== comp
.extension
]
295 print "Test %s / %s" % (comp
, dec
)
297 comp
.save_to_stream(data
, middle
)
299 dec
.save_to_stream(FileData(middle
.name
), out
)
301 assert file(out
.name
).read() == test_data
305 dir = '/tmp/archive-regression-test'
307 if not os
.path
.exists(dir): os
.mkdir(dir)
308 print >>file(dir + '/test', 'w'), test_data
311 for archive
in operations
:
312 if not isinstance(archive
, Archive
): continue
313 extract
= [o
for o
in operations
if isinstance(o
, Extract
) and
314 o
.extension
== archive
.extension
]
316 print "(skipping %s; no extractor)" % archive
319 if os
.path
.exists(out
): os
.system("rm -r '%s'" % out
)
321 assert len(extract
) == 1
323 print "Test %s / %s" % (archive
, extract
)
326 archive
.save_to_stream(data
, middle
)
327 extract
.save_to_file(FileData(middle
.name
), dir + '.out')
329 assert os
.listdir(dir) == os
.listdir(out
)
330 assert file(dir + '/test').read() == file(out
+ '/test').read()
333 os
.unlink(dir + '/test')
335 if os
.path
.exists(out
): os
.system("rm -r '%s'" % out
)
337 if __name__
== '__main__':
338 __builtins__
._ = rox
.i18n
.translation(os
.path
.join(rox
.app_dir
, 'Messages'))