Moved process stuff into a separate file, and added test cases.
[rox-archive.git] / AppRun
blobf9a45886a3ed729c45c1a68389f4a4822a54b087
1 #!/usr/bin/env python
3 import findrox
4 import sys, os, formats
6 import rox
7 from rox import g, TRUE, FALSE, saving
9 if len(sys.argv) != 2:
10 rox.info("Drag a file or directory onto Archive to archive it. "
11 "Drag an archive onto it to extract.")
12 sys.exit(0)
14 from support import escape, Tmp, pipe_through_command
16 # Show the savebox, so at least the user knows something is happening..
17 class FileData(saving.Saveable):
18 "A file on the local filesystem."
19 def __init__(self, path, stream):
20 self.path = path
21 self.start = stream.read(300)
22 try:
23 stream.seek(0)
24 self.stream = stream
25 except:
26 print "(unseekable)"
27 # Input is not a regular, local, seekable file, so copy it
28 # to a local temp file.
29 import shutil
30 tmp = Tmp()
31 tmp.write(self.start)
32 shutil.copyfileobj(stream, tmp)
33 self.stream = tmp
35 create_arc = ()
37 create_stream = (
38 ('test', "wibble", 'application/x-gzip'),
39 ('gz', "gzip -c -", 'application/x-gzip'),
40 ('bz2', "bzip2 -c -", 'application/x-bzip')
43 extract_arc = (
44 # Archives. Executed from new directory.
45 ('tgz', "gunzip -c - | tar xf -", 'inode/directory'),
46 ('tar.gz', "gunzip -c - | tar xf -", 'inode/directory'),
47 ('tar.bz', "bunzip2 -c - | tar xf -", 'inode/directory'),
48 ('tar.bz2', "bunzip2 -c - | tar xf -", 'inode/directory'),
49 ('zip', "unzip -", None),
50 ('rar', "rar x -", None),
51 ('tar', "tar xf -", None)
54 extract_stream = (
55 # Compressed streams
56 ('gz', "gunzip -c -", None),
57 ('bz', "bunzip2 -ck -", None),
58 ('bz2', "bunzip2 -ck -", None)
61 def need_dir(self, command):
62 return command in [c for e,c,t in self.extract_arc]
64 def save_to_file(self, file):
65 command = self.op
66 if self.need_dir(command):
67 os.mkdir(file)
68 os.chdir(file)
69 pipe_through_command(command, self.stream, None)
70 else:
71 saving.Saveable.save_to_file(self, file)
73 def save_to_stream(self, stream):
74 command = self.op
75 if self.need_dir(command):
76 raise Exception('Sorry, archives can only be extracted into '
77 'a local directory.')
78 self.stream.seek(0)
79 pipe_through_command(command, self.stream, stream)
81 def set_op(self, op):
82 self.op = op
84 class DirData(saving.Saveable):
85 def __init__(self, source):
86 self.path = source
87 self.start = None
89 create_arc = (
90 ('tgz', "tar cf - '%s' | gzip", 'application/x-compressed-tar'),
91 ('tar.gz', "tar cf - '%s' | gzip", 'application/x-compressed-tar'),
92 ('tar.bz', "tar cf - '%s' | bzip2", 'application/x-bzip-compressed-tar'),
93 ('tar.bz2', "tar cf - '%s' | bzip2", 'application/x-bzip-compressed-tar'),
94 ('zip', "zip -r - '%s'", 'application/zip'),
95 ('rar', "rar a - '%s'", 'application/x-rar'),
96 ('tar', "tar cf - '%s'", 'application/x-tar')
99 create_stream = ()
100 extract_arc = ()
101 extract_stream = ()
103 def save_to_stream(self, stream):
104 command = self.op % escape(self.path)
105 pipe_through_command(command, None, stream)
107 def set_op(self, op):
108 self.op = op
110 class ArchiveBox(saving.SaveBox):
111 def build_main_area(self):
112 self.vbox.add(self.save_area)
114 self.operation = g.OptionMenu()
115 self.vbox.pack_start(self.operation, FALSE, TRUE, 0)
116 self.operation.show()
117 self.operation.set_border_width(5)
118 self.updating = 0
120 self.ops_menu = g.Menu()
121 self.operation.set_menu(self.ops_menu)
123 def add_ops(self):
124 doc = self.save_area.document
125 self.i_to_op = []
126 for op, command, type in doc.create_arc:
127 item = g.MenuItem('Create .%s archive' % op)
128 item.show()
129 self.ops_menu.append(item)
130 self.i_to_op.append((op, command, type))
131 for op, command, type in doc.create_stream:
132 item = g.MenuItem('Compress as .%s' % op)
133 item.show()
134 self.ops_menu.append(item)
135 self.i_to_op.append((op, command, type))
137 if doc.start:
138 guess = formats.guess_format(doc.start)
139 else:
140 guess = None
142 if guess in ['gz', 'bz2', 'bz']:
143 if doc.path.endswith('.tar.' + guess):
144 guess = 'tar.' + guess
146 init = 0
147 for op, command, type in doc.extract_arc:
148 item = g.MenuItem('Extract from a .%s' % op)
149 item.show()
150 self.ops_menu.append(item)
151 if op == guess:
152 init = len(self.i_to_op)
153 self.i_to_op.append((op, command, type))
154 for op, command, type in doc.extract_stream:
155 item = g.MenuItem('Uncompress .%s' % op)
156 item.show()
157 self.ops_menu.append(item)
158 if op == guess:
159 init = len(self.i_to_op)
160 self.i_to_op.append((op, command, type))
162 name = doc.path
163 if guess and name.endswith('.' + guess):
164 name = name[:-len(guess)-1]
165 self.save_area.entry.set_text(name)
167 self.operation.connect('changed', self.op_changed)
168 self.save_area.entry.connect('changed', self.name_changed)
169 self.operation.set_history(init)
171 def name_changed(self, entry):
172 if self.updating:
173 return
174 self.updating = 1
176 name = entry.get_text()
177 doc = self.save_area.document
178 i = 0
179 for o, command, type in doc.create_arc + doc.create_stream:
180 if name.endswith('.' + o):
181 self.operation.set_history(i)
182 break
183 i += 1
185 self.updating = 0
187 def op_changed(self, operation):
188 i = operation.get_history()
189 doc = self.save_area.document
190 op = self.i_to_op[i]
191 doc.set_op(op[1])
192 if op[2]:
193 print op[2]
194 self.set_type(op[2])
195 else:
196 self.set_type('text/plain')
198 if self.updating:
199 return
200 self.updating = 1
202 name = self.save_area.entry.get_text()
203 if not name:
204 name = doc.path
205 for o, command, type in doc.create_arc + doc.create_stream:
206 if name.endswith('.' + o):
207 name = name[:-len(o)-1]
208 break
209 if i < len(doc.create_arc):
210 name += '.' + doc.create_arc[i][0]
211 else:
212 i -= len(doc.create_arc)
213 if i < len(doc.create_stream):
214 name += '.' + doc.create_stream[i][0]
215 self.save_area.entry.set_text(name)
217 self.updating = 0
219 # Check that our input is a regular local file.
220 # If not, fetch the data and put it in /tmp.
222 path = sys.argv[1]
224 source = None
226 if path == '-':
227 source = sys.stdin
228 else:
229 path = rox.get_local_path(path)
230 if not path:
231 rox.croak('Sorry, I can only extract/archive local files.')
232 if not os.path.isdir(path):
233 try:
234 source = file(path)
235 except:
236 rox.report_exception()
237 sys.exit(1)
239 if source:
240 data = FileData(path, source)
241 else:
242 data = DirData(path)
244 savebox = ArchiveBox(data, '', 'text/plain')
245 savebox.show()
246 g.gdk.flush()
248 savebox.add_ops()
250 rox.mainloop()