xref: /haiku/src/add-ons/tracker/zipomatic/ZipperThread.cpp (revision 1214ef1b2100f2b3299fc9d8d6142e46f70a4c3f)
1 /*
2  * Copyright 2003-2006, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Jonas Sundström, jonas.sundstrom@kirilla.com
7  */
8 
9 
10 #include "ZipperThread.h"
11 #include "ZipOMaticWindow.h"
12 #include "ZipOMaticMisc.h"
13 
14 #include <Debug.h>
15 #include <FindDirectory.h>
16 #include <Message.h>
17 #include <Path.h>
18 #include <Volume.h>
19 
20 #include <signal.h>
21 #include <unistd.h>
22 #include <errno.h>
23 
24 
25 const char* kZipperThreadName = "ZipperThread";
26 
27 
28 ZipperThread::ZipperThread (BMessage* refsMessage, BWindow* window)
29 	: GenericThread(kZipperThreadName, B_NORMAL_PRIORITY, refsMessage),
30 	fWindowMessenger(window),
31 	fZipProcess(-1),
32 	m_std_in(-1),
33 	m_std_out(-1),
34 	m_std_err(-1),
35 	fOutputFile(NULL)
36 {
37 	PRINT(("ZipperThread()\n"));
38 
39 	m_thread_data_store = new BMessage(*refsMessage);
40 		// leak?
41 		// prevents bug with B_SIMPLE_DATA
42 		// (drag&drop messages)
43 }
44 
45 
46 ZipperThread::~ZipperThread()
47 {
48 }
49 
50 
51 status_t
52 ZipperThread::ThreadStartup()
53 {
54 	PRINT(("ZipperThread::ThreadStartup()\n"));
55 
56 	BString archiveName = "Archive.zip";
57 
58 	// do all refs have the same parent dir?
59 	type_code type = B_REF_TYPE;
60 	int32 refCount = 0;
61 	entry_ref ref;
62 	entry_ref lastRef;
63 	bool sameFolder = true;
64 
65 	status_t status = m_thread_data_store->GetInfo("refs", &type, &refCount);
66 	if (status != B_OK)
67 		return status;
68 
69 	for (int index = 0;	index < refCount; index++) {
70 		m_thread_data_store->FindRef("refs", index, &ref);
71 
72 		if (index > 0) {
73 			BEntry entry(&ref);
74 			if (entry.IsSymLink()) {
75 				entry.SetTo(&ref, true);
76 				entry_ref target;
77 				entry.GetRef(&target);
78 				if (lastRef.directory != target.directory) {
79 					sameFolder = false;
80 					break;
81 				}
82 			} else if (lastRef.directory != ref.directory) {
83 				sameFolder = false;
84 				break;
85 			}
86 		}
87 		lastRef = ref;
88 	}
89 
90 	// change active dir
91 	if (sameFolder) {
92 		BEntry entry(&lastRef);
93 		BPath path;
94 		entry.GetParent(&entry);
95 		entry.GetPath(&path);
96 		chdir(path.Path());
97 	} else {
98 		BPath path;
99 		if (find_directory(B_DESKTOP_DIRECTORY, &path) == B_OK)
100 			chdir(path.Path());
101 	}
102 
103 	// archive filename
104 	if (refCount == 1) {
105 		archiveName = lastRef.name;
106 		archiveName += ".zip";
107 	}
108 
109 	int32 argc = refCount + 3;
110 	const char** argv = new const char* [argc + 1];
111 
112 	argv[0] = strdup("/bin/zip");
113 	argv[1] = strdup("-ry");
114 	argv[2] = strdup(archiveName.String());
115 
116 	// files to zip
117 	for (int index = 0;  index < refCount ;  index++) {
118 		m_thread_data_store->FindRef("refs", index, &ref);
119 
120 		if (sameFolder) {
121 			// just the file name
122 			argv[3 + index]	= strdup(ref.name);
123 		} else {
124 			// full path
125 			BPath path(&ref);
126 			BString file = path.Path();
127 			argv[3 + index]	= strdup(path.Path());
128 		}
129 	}
130 
131 	argv[argc] = NULL;
132 
133 	fZipProcess = _PipeCommand(argc, argv, m_std_in, m_std_out, m_std_err);
134 
135 	delete [] argv;
136 
137 	if (fZipProcess < 0)
138 		return fZipProcess;
139 
140 	resume_thread(fZipProcess);
141 
142 	fOutputFile = fdopen(m_std_out, "r");
143 	if (fOutputFile == NULL)
144 		return errno;
145 
146 	archiveName.Prepend("Creating archive: ");
147 
148 	_SendMessageToWindow('strt', "archive_filename", archiveName.String());
149 	_SendMessageToWindow('outp', "zip_output", "Preparing to archive");
150 
151 	PRINT(("\n"));
152 
153 	return B_OK;
154 }
155 
156 
157 status_t
158 ZipperThread::ExecuteUnit()
159 {
160 	//PRINT(("ZipperThread::ExecuteUnit()\n"));
161 
162 	// read output from /bin/zip
163 	// send it to window
164 	char buffer[4096];
165 
166 	char* output = fgets(buffer, sizeof(buffer) - 1, fOutputFile);
167 	if (output == NULL)
168 		return EOF;
169 
170 	char* newLine = strrchr(output, '\n');
171 	if (newLine != NULL)
172 		*newLine = '\0';
173 
174 	if (!strncmp("  a", output, 3)) {
175 		output[2] = 'A';
176 		_SendMessageToWindow('outp', "zip_output", output + 2);
177 	} else if (!strncmp("up", output, 2)) {
178 		output[0] = 'U';
179 		_SendMessageToWindow('outp', "zip_output", output);
180 	} else {
181 		_SendMessageToWindow('outp', "zip_output", output);
182 	}
183 
184 	return B_OK;
185 }
186 
187 
188 status_t
189 ZipperThread::ThreadShutdown()
190 {
191 	PRINT(("ZipperThread::ThreadShutdown()\n"));
192 
193 	close(m_std_in);
194     close(m_std_out);
195    	close(m_std_err);
196 
197 	return B_OK;
198 }
199 
200 
201 void
202 ZipperThread::ThreadStartupFailed(status_t status)
203 {
204 	error_message("ZipperThread::ThreadStartupFailed() \n", status);
205 	Quit();
206 }
207 
208 
209 void
210 ZipperThread::ExecuteUnitFailed(status_t status)
211 {
212 	error_message("ZipperThread::ExecuteUnitFailed() \n", status);
213 
214 	if (status == EOF) {
215 		// thread has finished, been quit or killed, we don't know
216 		_SendMessageToWindow('exit');
217 	} else {
218 		// explicit error - communicate error to Window
219 		_SendMessageToWindow('exrr');
220 	}
221 
222 	Quit();
223 }
224 
225 
226 void
227 ZipperThread::ThreadShutdownFailed(status_t status)
228 {
229 	error_message("ZipperThread::ThreadShutdownFailed() \n", status);
230 }
231 
232 
233 void
234 ZipperThread::_MakeShellSafe(BString* string)
235 {
236 	string->CharacterEscape("\"$`", '\\');
237 	string->Prepend("\"");
238 	string->Append("\"");
239 }
240 
241 
242 status_t
243 ZipperThread::ProcessRefs(BMessage* msg)
244 {
245 	return B_OK;
246 }
247 
248 
249 thread_id
250 ZipperThread::_PipeCommand(int argc, const char** argv, int& in, int& out,
251 	int& err, const char** envp)
252 {
253 	// This function was originally written by Peter Folk <pfolk@uni.uiuc.edu>
254 	// and published in the BeDevTalk FAQ
255 	// http://www.abisoft.com/faq/BeDevTalk_FAQ.html#FAQ-209
256 
257 	thread_id thread;
258 
259 	// Save current FDs
260 	int oldIn = dup(STDIN_FILENO);
261 	int oldOut = dup(STDOUT_FILENO);
262 	int oldErr = dup(STDERR_FILENO);
263 
264 	int inPipe[2], outPipe[2], errPipe[2];
265 
266 	// Create new pipe FDs as stdin, stdout, stderr
267 	if (pipe(inPipe) < 0)
268 		goto err1;
269 	if (pipe(outPipe) < 0)
270 		goto err2;
271 	if (pipe(errPipe) < 0)
272 		goto err3;
273 
274 	errno = 0;
275 
276 	// replace old stdin/stderr/stdout
277 	dup2(inPipe[0], STDIN_FILENO);
278 	close(inPipe[0]);
279 	dup2(outPipe[1], STDOUT_FILENO);
280 	close(outPipe[1]);
281 	dup2(errPipe[1], STDERR_FILENO);
282 	close(errPipe[1]);
283 
284 	if (errno == 0) {
285 		in = inPipe[1];		// Write to in, appears on cmd's stdin
286 		out = outPipe[0];	// Read from out, taken from cmd's stdout
287 		err = errPipe[0];	// Read from err, taken from cmd's stderr
288 
289 		// execute command
290 		thread = load_image(argc, argv, envp);
291 
292 		PRINT(("load_image() thread_id: %ld\n", ret));
293 	} else
294 		thread = errno;
295 
296 	// Restore old FDs
297 	dup2(oldIn, STDIN_FILENO);
298 	close(oldIn);
299 	dup2(oldOut, STDOUT_FILENO);
300 	close(oldOut);
301 	dup2(oldErr, STDERR_FILENO);
302 	close(oldErr);
303 	return thread;
304 
305 err3:
306 	close(outPipe[0]);
307 	close(outPipe[1]);
308 err2:
309 	close(inPipe[0]);
310 	close(inPipe[1]);
311 err1:
312 	close(oldIn);
313 	close(oldOut);
314 	close(oldErr);
315 	return errno;
316 }
317 
318 
319 void
320 ZipperThread::_SendMessageToWindow(uint32 what, const char* name, const char* value)
321 {
322 	BMessage msg(what);
323 	if (name != NULL && value != NULL)
324 		msg.AddString(name, value);
325 
326 	fWindowMessenger.SendMessage(&msg);
327 }
328 
329 
330 status_t
331 ZipperThread::SuspendExternalZip()
332 {
333 	PRINT(("ZipperThread::SuspendExternalZip()\n"));
334 
335 	thread_info info;
336 	status_t status = get_thread_info(fZipProcess, &info);
337 
338 	if (status == B_OK && !strcmp(info.name, "zip"))
339 		return suspend_thread(fZipProcess);
340 
341 	return status;
342 }
343 
344 
345 status_t
346 ZipperThread::ResumeExternalZip()
347 {
348 	PRINT(("ZipperThread::ResumeExternalZip()\n"));
349 
350 	thread_info info;
351 	status_t status = get_thread_info(fZipProcess, &info);
352 
353 	if (status == B_OK && !strcmp(info.name, "zip"))
354 		return resume_thread(fZipProcess);
355 
356 	return status;
357 }
358 
359 
360 status_t
361 ZipperThread::InterruptExternalZip()
362 {
363 	PRINT(("ZipperThread::InterruptExternalZip()\n"));
364 
365 	thread_info info;
366 	status_t status = get_thread_info(fZipProcess, &info);
367 
368 	if (status == B_OK && !strcmp(info.name, "zip")) {
369 		status = B_OK;
370 		status = send_signal(fZipProcess, SIGINT);
371 		WaitOnExternalZip();
372 		return status;
373 	}
374 
375 	return status;
376 }
377 
378 
379 status_t
380 ZipperThread::WaitOnExternalZip()
381 {
382 	PRINT(("ZipperThread::WaitOnExternalZip()\n"));
383 
384 	thread_info info;
385 	status_t status = get_thread_info(fZipProcess, &info);
386 
387 	if (status == B_OK && !strcmp(info.name, "zip"))
388 		return wait_for_thread(fZipProcess, &status);
389 
390 	return status;
391 }
392