1 if __name__
== '__main__':
2 import findrox
; findrox
.version(1, 99, 11)
4 from support
import shell_escape
, Tmp
6 from rox
.processes
import PipeThroughCommand
10 def pipe_through_command(command
, src
, dst
):
11 global current_command
12 assert not current_command
17 current_command
= PipeThroughCommand(command
, src
, dst
)
19 current_command
.wait()
21 current_command
= None
27 def __init__(self
, extension
):
28 operations
.append(self
)
29 self
.extension
= extension
31 def can_handle(self
, data
):
32 return isinstance(data
, FileData
)
34 def save_to_stream(self
, data
, stream
):
35 pipe_through_command(self
.command
, data
.source
, stream
)
37 class Compress(Operation
):
38 "Compress a stream into another stream."
41 def __init__(self
, extension
, command
, type):
42 Operation
.__init
__(self
, extension
)
43 self
.command
= command
47 return _('Compress as .%s') % self
.extension
49 class Decompress(Operation
):
50 "Decompress a stream into another stream."
53 def __init__(self
, extension
, command
):
54 Operation
.__init
__(self
, extension
)
55 self
.command
= command
58 return _('Decompress .%s') % self
.extension
60 class Extract(Operation
):
61 "Extract an archive to a directory."
62 type = 'inode/directory'
64 def __init__(self
, extension
, command
):
65 "If command has a %s then the source path is inserted, else uses stdin."
66 Operation
.__init
__(self
, extension
)
67 self
.command
= command
70 return _('Extract from a .%s') % self
.extension
72 def save_to_stream(self
, data
, stream
):
73 raise Exception(_('This operation creates a directory, so you have '
74 'to drag to a filer window on the local machine'))
76 def save_to_file(self
, data
, path
):
77 if os
.path
.exists(path
):
78 if not os
.path
.isdir(path
):
79 raise Exception(_("'%s' already exists and is not a directory!") %
81 if not os
.path
.exists(path
):
84 command
= self
.command
86 if command
.find("'%s'") != -1:
87 command
= command
% shell_escape(source
.name
)
90 pipe_through_command(command
, source
, None)
93 os
.rmdir(path
) # Will only succeed if it's empty
96 if os
.path
.exists(path
):
99 def pull_up(self
, path
):
100 # If we created only a single subdirectory, move it up.
101 dirs
= os
.listdir(path
)
105 unneeded_path
= os
.path
.join(path
, dir)
106 if not os
.path
.isdir(unneeded_path
):
109 tmp_path
= os
.path
.join(path
, 'tmp-' + `random
.randint(0, 100000)`
)
110 os
.rename(unneeded_path
, tmp_path
)
111 for file in os
.listdir(tmp_path
):
112 os
.rename(os
.path
.join(tmp_path
, file), os
.path
.join(path
, file))
115 class Archive(Operation
):
116 "Create an archive from a directory."
119 def __init__(self
, extension
, command
, type):
120 assert command
.find("'%s'") != -1
122 Operation
.__init
__(self
, extension
)
123 self
.command
= command
127 return _('Create .%s archive') % self
.extension
129 def can_handle(self
, data
):
130 return isinstance(data
, DirData
)
132 def save_to_stream(self
, data
, stream
):
133 os
.chdir(os
.path
.dirname(data
.path
))
134 command
= self
.command
% shell_escape(os
.path
.basename(data
.path
))
135 pipe_through_command(command
, None, stream
)
137 tgz
= Extract('tgz', "gunzip -c - | tar xf -")
138 tbz
= Extract('tar.bz2', "bunzip2 -c - | tar xf -")
139 tarz
= Extract('tar.Z', "uncompress -c - | tar xf -")
140 tlz
= Extract('tlz', "unlzma -c - | tar xf -")
141 txz
= Extract('txz', "unxz -c - | tar xf -")
142 rar
= Extract('rar', "unrar x '%s'")
143 ace
= Extract('ace', "unace x '%s'")
144 tar
= Extract('tar', "tar xf -")
145 rpm
= Extract('rpm', "rpm2cpio - | cpio -id --quiet")
146 cpio
= Extract('cpio', "cpio -id --quiet")
147 deb
= Extract('deb', "ar x '%s'")
148 zip = Extract('zip', "unzip -q '%s'")
149 jar
= Extract('jar', "unzip -q '%s'")
150 lha
= Extract('lha', "lha x '%s'")
152 make_tgz
= Archive('tgz', "tar cf - '%s' | gzip", 'application/x-compressed-tar')
153 Archive('tar.gz', "tar cf - '%s' | gzip", 'application/x-compressed-tar')
154 Archive('tar.bz2', "tar cf - '%s' | bzip2", 'application/x-bzip-compressed-tar')
155 Archive('tlz', "tar cf - '%s' | lzma", 'application/x-lzma-compressed-tar')
156 Archive('tar.lzma',"tar cf - '%s' | lzma", 'application/x-lzma-compressed-tar')
157 Archive('txz', "tar cf - '%s' | xz", 'application/x-xz-compressed-tar')
158 Archive('tar.xz', "tar cf - '%s' | xz", 'application/x-xz-compressed-tar')
159 Archive('zip', "zip -qr - '%s'", 'application/zip'),
160 Archive('jar', "zip -qr - '%s'", 'application/x-jar')
161 Archive('tar', "tar cf - '%s'", 'application/x-tar')
162 Archive('lha', "lha c - '%s'", 'application/x-lha'),
164 # Note: these go afterwards so that .tar.gz matches before .gz
165 make_gz
= Compress('gz', "gzip -c -", 'application/x-gzip')
166 Compress('bz2', "bzip2 -c -", 'application/x-bzip')
167 Compress('lzma', "lzma -c -", 'application/x-lzma')
168 Compress('xz', "xz -c -", 'application/x-xz')
169 Compress('uue', "uuencode /dev/stdout", 'application/x-uuencoded')
171 gz
= Decompress('gz', "gunzip -c -")
172 bz2
= Decompress('bz2', "bunzip2 -ck -")
173 uue
= Decompress('uue', "uudecode -o /dev/stdout")
174 z
= Decompress('Z', "uncompress -c -")
175 lzma
= Decompress('lzma', "unlzma -c -")
176 xz
= Decompress('xz', "unxz -c -")
179 # Can bzip2 read bzip files?
190 known_extensions
= {}
193 known_extensions
[x
.extension
] = None
194 except AttributeError:
198 "A file on the local filesystem."
200 def __init__(self
, path
):
208 self
.mode
= os
.stat(path
).st_mode
210 rox
.report_exception()
214 start
= source
.read(300)
216 if source
is sys
.stdin
:
217 raise Exception("Always copy stdin!")
221 # Input is not a regular, local, seekable file, so copy it
222 # to a local temp file.
227 shutil
.copyfileobj(source
, tmp
)
231 self
.default
= self
.guess_format(start
)
237 for ext
in known_extensions
:
238 if path
.endswith('.' + ext
):
239 new
= path
[:-len(ext
)-1]
240 if len(new
) < len(name
):
242 if self
.default
.add_extension
:
243 name
+= '.' + self
.default
.extension
246 # Default name is same as input. Change it somehow...
247 if '.' in os
.path
.basename(name
):
248 name
= name
[:name
.rindex('.')]
252 self
.default_name
= name
254 def guess_format(self
, data
):
255 "Return a good default Operation, judging by the first 300 bytes or so."
257 def string(offset
, match
):
258 return data
[offset
:offset
+ len(match
)] == match
259 def short(offset
, match
):
263 return ((a
== match
& 0xff) and (b
== (match
>> 8))) or \
264 (b
== match
& 0xff) and (a
== (match
>> 8))
268 if string(257, 'ustar\0') or string(257, 'ustar\040\040\0'):
270 if short(0, 070707) or short(0, 0143561) or string(0, '070707') or \
271 string(0, '070701') or string(0, '070702'):
273 if string(0, '!<arch>') or string(0, '\\<ar>') or string(0, '<ar>'):
274 if string(7, '\ndebian'):
276 if string(0, 'Rar!'): return rar
277 if string(7, '**ACE**'): return ace
278 if string(0, 'PK\003\004'): return zip
279 if string(0, 'PK00'): return zip
280 if string(0, '\xed\xab\xee\xdb'): return rpm
281 if (string(2, '-lz') or string(2, '-lh')) and data
[6] == '-':
285 if string(0, '\037\213'):
286 if self
.path
.endswith('.tar.gz') or self
.path
.endswith('.tgz'):
289 if string(0, 'BZh') or string(0, 'BZ'):
290 if self
.path
.endswith('.tar.bz') or self
.path
.endswith('.tar.bz2') or \
291 self
.path
.endswith('.tbz') or self
.path
.endswith('.tbz2'):
294 if string(0, ']\0\0') and (0 == ord(data
[3]) & 0x7f):
295 if self
.path
.endswith('.tar.lzma') or self
.path
.endswith('.tlz'):
298 if string(0, '\3757zXZ\0'):
299 if self
.path
.endswith('.tar.xz') or self
.path
.endswith('.txz'):
302 if string(0, 'begin '):
304 if string(0, '\037\235'):
305 if self
.path
.endswith('.tar.Z'):
313 def __init__(self
, path
):
315 self
.default
= make_tgz
316 self
.default_name
= path
+ '.' + self
.default
.extension