Removed the pyc files (oops!).
[rox-archive.git] / Archive.py
blobca188af1fd04525a34b24d0886e9a17206c51845
1 from string import rfind
2 import os.path
3 import re
4 import stat
5 import os
6 import signal
7 import fcntl
9 from gtk import *
10 from GDK import *
12 from SaveBox import SaveBox
13 from support import *
14 import choices
15 import string
17 child_pid = None
19 archive_formats = [
20 # Extension, extract, create
21 ('.tar', "tar xf '%s'", "tar cf '%(dst)s' '%(src)s'"),
22 ('.zip', "unzip '%s'", "zip -r '%(dst)s' '%(src)s'"),
23 ('.deb', "ar x '%s'", None),
25 ('.tgz', "gunzip -c '%s' | tar xf -",
26 "tar cf - '%(src)s' | gzip > '%(dst)s'"),
27 ('.tar.gz', "gunzip -c '%s' | tar xf -",
28 "tar cf - '%(src)s' | gzip > '%(dst)s'"),
30 ('.tbz', "bunzip2 -c '%s' | tar xf -",
31 "tar cf - '%(src)s' | bzip2 > '%(dst)s'"),
32 ('.tar.bz', "bunzip2 -c '%s' | tar xf -",
33 "tar cf - '%(src)s' | bzip2 > '%(dst)s'"),
34 ('.tbz2', "bunzip2 -c '%s' | tar xf -",
35 "tar cf - '%(src)s' | bzip2 > '%(dst)s'"),
36 ('.tar.bz2', "bunzip2 -c '%s' | tar xf -",
37 "tar cf - '%(src)s' | bzip2 > '%(dst)s'")
40 compressed_formats = [
41 # Ext, extract, compress, type
42 ('.gz', "gunzip -c '%s'", "gzip -c '%s'", 'x-gzip'),
43 ('.bz', "bunzip2 -ck '%s'", "bzip2 -c '%s'", 'x-bzip'),
44 ('.bz2', "bunzip2 -ck '%s'", "bzip2 -c '%s'", 'x-bzip')
47 def get_format(path, formats):
48 for f in formats:
49 ext = f[0]
50 if ext == path[-len(ext):]:
51 return f
52 return None
54 def esc(text):
55 """Return text with \ and ' escaped"""
56 return re.sub("'", "'\"'\"'", text)
58 def bg_system(command, out = None):
59 "system(command), but still process GUI events while waiting..."
60 "If 'out' is set then the child's stdout goes to that FD. 'out' is "
61 "closed in the parent process."
62 global child_pid
64 (r, w) = os.pipe()
66 child_pid = fork()
67 if child_pid == -1:
68 os.close(r)
69 os.close(w)
70 if out != None:
71 os.close(out)
72 report_error("fork() failed!")
73 return 127
74 if child_pid == 0:
75 # Child
76 try:
77 if out != None:
78 os.dup2(out, 1)
80 # Collect stderr...
81 if w != 2:
82 os.dup2(w, 2)
83 os.close(w)
85 os.setpgid(0, 0) # Start a new process group
87 os.close(r)
88 error = os.system(command) != 0
89 os._exit(error)
90 except:
91 pass
92 os._exit(127)
94 if out != None:
95 os.close(out)
96 os.close(w)
98 done = [""]
99 def cb(src, cond, done = done):
100 data = os.read(src, 100)
101 if data:
102 done[0] = done[0] + data
103 else:
104 done.append(1)
105 tag = input_add(r, INPUT_READ, cb)
107 while len(done) < 2:
108 mainiteration()
110 input_remove(tag)
112 os.close(r)
113 (pid, status) = waitpid(child_pid, 0)
114 child_pid = None
116 str = string.strip(done[0])
117 if status or str:
118 if str:
119 report_error("Error: " + str)
120 else:
121 report_error("Operation failed")
123 return status
125 def make_archiver(path):
126 while path[-1:] == '/':
127 path = path[:-1]
128 if os.path.isdir(path):
129 window = ArchiveDir(path)
130 else:
131 window = None
132 f = get_format(path, archive_formats)
133 if f:
134 window = ExtractDir(path, f)
135 else:
136 f = get_format(path, compressed_formats)
137 if f:
138 window = ExtractFile(path, f)
139 else:
140 window = ArchiveFile(path)
142 window.connect('destroy', mainquit)
143 window.show()
145 def pull_up(dir):
146 "If dir contains only one subdir, move its contents into dir."
147 list = os.listdir(dir)
148 if len(list) != 1:
149 return
151 subdir = os.path.join(dir, list[0])
152 if not os.path.isdir(subdir):
153 return
155 for file in os.listdir(subdir):
156 bg_system("mv '%s' ." % esc(os.path.join(subdir, file)))
157 os.rmdir(subdir)
159 def report_known(formats):
160 txt = "Allowed extensions are:\n"
161 for f in formats:
162 if f[2]:
163 txt = txt + f[0] + '\n'
164 report_error(txt)
166 class Archive(SaveBox):
167 def __init__(self, win, media, sub):
168 SaveBox.__init__(self, win, media, sub);
169 self.connect('destroy', self.destroyed)
171 def destroyed(self, widget):
172 global child_pid
174 if child_pid:
175 os.kill(-child_pid, signal.SIGTERM)
177 def save_as(self, path):
178 self.set_sensitive(FALSE);
179 success = FALSE
180 try:
181 success = self.do_save(path)
182 except:
183 success = FALSE
184 report_exception()
185 self.set_sensitive(TRUE);
186 return success
188 def set_uri(self, uri):
189 path = get_local_path(uri)
190 if path:
191 child('rox', '-x', path)
192 pass
194 # save_as(path) - write data to file, TRUE on success
195 # set_uri(uri) - data is safely saved to this location
197 class ExtractDir(Archive):
198 def __init__(self, path, format):
199 self.path = path
200 ext = format[0]
201 self.extract = format[1]
202 self.uri = path[:-len(ext)]
203 Archive.__init__(self, self, 'special', 'directory')
205 def do_save(self, path):
206 os.mkdir(path)
207 os.chdir(path)
208 if bg_system(self.extract % esc(self.path)):
209 return FALSE
210 else:
211 pull_up(path)
212 return TRUE
214 class ExtractFile(Archive):
215 def __init__(self, path, format):
216 self.path = path
217 ext = format[0]
218 self.extract = format[1]
219 self.uri = path[:-len(ext)]
220 Archive.__init__(self, self, 'text', 'plain')
222 def do_save(self, path):
223 if os.path.exists(path):
224 report_error("File `%s' already exists!" % path)
225 return FALSE
226 stats = os.stat(self.path)
227 mode = stat.S_IMODE(stats[stat.ST_MODE]) & 0x1ff;
228 out = os.open(path, os.O_WRONLY | os.O_CREAT, mode)
229 return bg_system(self.extract % esc(self.path), out = out) == 0
231 def send_raw(self, selection_data):
232 (r, w) = os.pipe()
233 self.data = ""
235 def cb(src, cond, self = self):
236 self.data = self.data + os.read(src, 1024)
237 input_add(r, INPUT_READ, cb)
239 bg_system(self.extract % esc(self.path), out = w)
241 while 1:
242 new = os.read(r, 1024)
243 if not new:
244 break
245 self.data = self.data + new
246 os.close(r)
247 selection_data.set(selection_data.target, 8, self.data)
248 self.data = ""
250 class ArchiveFile(Archive):
251 def __init__(self, path):
252 self.path = path
253 self.uri = path + '.gz'
254 Archive.__init__(self, self, 'application', 'x-gzip')
256 def do_save(self, path):
257 format = get_format(path, compressed_formats)
258 if not format or not format[2]:
259 report_known(compressed_formats)
260 return FALSE
262 if os.path.exists(path):
263 report_error("File `%s' already exists!" % path)
264 return FALSE
265 stats = os.stat(self.path)
266 mode = stat.S_IMODE(stats[stat.ST_MODE]) & 0x1ff;
267 out = os.open(path, os.O_WRONLY | os.O_CREAT, mode)
268 return bg_system(format[2] % esc(self.path), out = out) == 0
270 class ArchiveDir(Archive):
271 def __init__(self, path):
272 self.path = path
273 self.uri = path + '.tgz'
274 Archive.__init__(self, self, 'application', 'x-compressed-tar')
276 def do_save(self, path):
277 format = get_format(path, archive_formats)
278 if not format or not format[2]:
279 report_known(archive_formats)
280 return FALSE
282 os.chdir(os.path.dirname(self.uri))
283 retval = bg_system(format[2] % {
284 'src' : esc(os.path.basename(self.path)),
285 'dst' : esc(path)
287 return retval == 0