4 import sys
, os
, formats
7 from rox
import g
, TRUE
, FALSE
, saving
10 rox
.info("Drag a file or directory onto Archive to archive it. "
11 "Drag an archive onto it to extract.")
15 """Return text with \ and ' escaped"""
16 return text
.replace("\\", "\\\\").replace("'", "\\'")
18 assert escape(''' a test ''') == ' a test '
19 assert escape(''' "a's test" ''') == ''' "a\\'s test" '''
20 assert escape(''' "a\\'s test" ''') == ''' "a\\\\\\'s test" '''
23 # Protect against DoS attacks
25 name
= `random
.randint(1, 1000000)`
+ '-archive'
28 return tempfile
.TemporaryFile(suffix
= name
)
30 def pipe_through_command(command
, src
, dst
):
31 """Execute 'command' with src as stdin (if any) and writing to
32 stream dst. src must be a fileno() stream."""
35 if hasattr(dst
, 'fileno'):
39 fd
= tmp_stream
.fileno()
48 os
.dup2(src
.fileno(), 0)
55 pid
, status
= os
.waitpid(child
, 0)
57 rox
.alert('Command returned an error!')
59 if dst
and tmp_stream
is not dst
:
62 data
= tmp_stream
.read()
65 dst
.write(tmp_stream
.read())
68 # Show the savebox, so at least the user knows something is happening..
69 class FileData(saving
.Saveable
):
70 "A file on the local filesystem."
71 def __init__(self
, path
, stream
):
73 self
.start
= stream
.read(300)
79 # Input is not a regular, local, seekable file, so copy it
80 # to a local temp file.
84 shutil
.copyfileobj(stream
, tmp
)
96 ('tgz', "gunzip -c '%s' | tar xf -"),
97 ('tar.gz', "gunzip -c '%s' | tar xf -"),
98 ('tar.bz', "bunzip2 -c '%s' | tar xf -"),
99 ('tar.bz2', "bunzip2 -c '%s' | tar xf -"),
100 ('zip', "unzip '%s'"),
101 ('rar', "rar x '%s'"),
102 ('tar', "tar xf '%s'")
107 ('gz', "gunzip -c -"),
108 ('bz', "bunzip2 -ck -"),
109 ('bz2', "bunzip2 -ck -")
112 def save_to_file(self
, file):
114 if command
.find('%s') != -1:
115 return pipe_through_command(command
% file,
118 return Saveable
.save_to_file(self
, file)
120 def save_to_stream(self
, stream
):
122 if command
.find('%s') != -1:
123 raise Exception('Sorry, archives can only be extracted into '
124 'a local directory.')
126 pipe_through_command(command
, self
.stream
, stream
)
128 def set_op(self
, op
):
131 class DirData(saving
.Saveable
):
132 def __init__(self
, source
):
137 ('tgz', "tar cf - '%s' | gzip"),
138 ('tar.gz', "tar cf - '%s' | gzip"),
139 ('tar.bz', "tar cf - '%s' | bzip2"),
140 ('tar.bz2', "tar cf - '%s' | bzip2"),
141 ('zip', "zip -r - '%s'"),
142 ('rar', "rar a - '%s'"),
143 ('tar', "tar cf - '%s'")
150 def save_to_stream(self
, stream
):
151 command
= self
.op
% escape(self
.path
)
152 pipe_through_command(command
, None, stream
)
154 def set_op(self
, op
):
157 class ArchiveBox(saving
.SaveBox
):
158 def build_main_area(self
):
159 self
.vbox
.add(self
.save_area
)
161 self
.operation
= g
.OptionMenu()
162 self
.vbox
.pack_start(self
.operation
, FALSE
, TRUE
, 0)
163 self
.operation
.show()
164 self
.operation
.set_border_width(5)
167 self
.ops_menu
= g
.Menu()
168 self
.operation
.set_menu(self
.ops_menu
)
171 doc
= self
.save_area
.document
173 for op
, command
in doc
.create_arc
:
174 item
= g
.MenuItem('Create .%s archive' % op
)
176 self
.ops_menu
.append(item
)
177 self
.i_to_op
.append(command
)
178 for op
, command
in doc
.create_stream
:
179 item
= g
.MenuItem('Compress as .%s' % op
)
181 self
.ops_menu
.append(item
)
182 self
.i_to_op
.append(command
)
185 guess
= formats
.guess_format(doc
.start
)
189 if guess
in ['gz', 'bz2', 'bz']:
190 if doc
.path
.endswith('.tar.' + guess
):
191 guess
= 'tar.' + guess
194 for op
, command
in doc
.extract_arc
:
195 item
= g
.MenuItem('Extract from a .%s' % op
)
197 self
.ops_menu
.append(item
)
199 init
= len(self
.i_to_op
)
200 self
.i_to_op
.append(command
)
201 for op
, command
in doc
.extract_stream
:
202 item
= g
.MenuItem('Uncompress .%s' % op
)
204 self
.ops_menu
.append(item
)
206 init
= len(self
.i_to_op
)
207 self
.i_to_op
.append(command
)
210 if name
.endswith('.' + guess
):
211 name
= name
[:-len(guess
)-1]
212 self
.save_area
.entry
.set_text(name
)
214 self
.operation
.connect('changed', self
.op_changed
)
215 self
.save_area
.entry
.connect('changed', self
.name_changed
)
216 self
.operation
.set_history(init
)
218 def name_changed(self
, entry
):
223 name
= entry
.get_text()
224 doc
= self
.save_area
.document
226 for o
, command
in doc
.create_arc
+ doc
.create_stream
:
227 if name
.endswith('.' + o
):
228 self
.operation
.set_history(i
)
234 def op_changed(self
, operation
):
239 i
= operation
.get_history()
240 doc
= self
.save_area
.document
241 doc
.set_op(self
.i_to_op
[i
])
242 name
= self
.save_area
.entry
.get_text()
245 for o
, command
in doc
.create_arc
+ doc
.create_stream
:
246 if name
.endswith('.' + o
):
247 name
= name
[:-len(o
)-1]
249 if i
< len(doc
.create_arc
):
250 name
+= '.' + doc
.create_arc
[i
][0]
252 i
-= len(doc
.create_arc
)
253 if i
< len(doc
.create_stream
):
254 name
+= '.' + doc
.create_stream
[i
][0]
255 self
.save_area
.entry
.set_text(name
)
259 # Check that our input is a regular local file.
260 # If not, fetch the data and put it in /tmp.
269 path
= rox
.get_local_path(path
)
271 rox
.croak('Sorry, I can only extract/archive local files.')
272 if not os
.path
.isdir(path
):
276 rox
.report_exception()
280 data
= FileData(path
, source
)
284 savebox
= ArchiveBox(data
, '', 'text/plain')