* Copyright 2003-2009, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Jonas SundstrΓΆm, jonas@kirilla.com
* Peter Folk <pfolk@uni.uiuc.edu>
*/
#include "ZipperThread.h"
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <Catalog.h>
#include <FindDirectory.h>
#include <Locale.h>
#include <Locker.h>
#include <Message.h>
#include <Path.h>
#include <Volume.h>
#include <private/tracker/tracker_private.h>
#include "ZipOMaticMisc.h"
#include "ZipOMaticWindow.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "file:ZipperThread.cpp"
ZipperThread::ZipperThread(BMessage* refsMessage, BWindow* window)
:
GenericThread("ZipperThread", B_NORMAL_PRIORITY, refsMessage),
fWindowMessenger(window),
fZipProcess(-1),
fStdIn(-1),
fStdOut(-1),
fStdErr(-1),
fOutputFile(NULL)
{
fThreadDataStore = new BMessage(*refsMessage);
}
ZipperThread::~ZipperThread()
{
}
status_t
ZipperThread::ThreadStartup()
{
type_code type = B_REF_TYPE;
int32 refCount = 0;
entry_ref ref;
entry_ref lastRef;
bool sameFolder = true;
status_t status = fThreadDataStore->GetInfo("refs", &type, &refCount);
if (status != B_OK || refCount < 1) {
_SendMessageToWindow(ZIPPO_THREAD_EXIT_ERROR);
Quit();
return B_ERROR;
}
for (int index = 0; index < refCount; index++) {
fThreadDataStore->FindRef("refs", index, &ref);
if (index > 0) {
if (lastRef.directory != ref.directory) {
sameFolder = false;
break;
}
}
lastRef = ref;
}
entry_ref dirRef;
bool gotDirRef = false;
status = fThreadDataStore->FindRef("dir_ref", 0, &dirRef);
if (status == B_OK) {
BEntry dirEntry(&dirRef);
BNode dirNode(&dirRef);
if (dirEntry.InitCheck() == B_OK
&& dirEntry.Exists()
&& dirNode.InitCheck() == B_OK
&& dirNode.IsDirectory())
gotDirRef = true;
}
if (gotDirRef) {
BEntry entry(&dirRef);
BPath path;
entry.GetPath(&path);
chdir(path.Path());
} else if (sameFolder) {
BEntry entry(&lastRef);
BPath path;
entry.GetParent(&entry);
entry.GetPath(&path);
chdir(path.Path());
} else {
BPath path;
if (find_directory(B_DESKTOP_DIRECTORY, &path) == B_OK)
chdir(path.Path());
}
BString archiveName;
if (refCount > 1)
archiveName = B_TRANSLATE("Archive");
else
archiveName = lastRef.name;
int index = 1;
for (;; index++) {
BString tryName = archiveName;
if (index != 1)
tryName << " " << index;
tryName << ".zip";
BEntry entry(tryName.String());
if (!entry.Exists()) {
archiveName = tryName;
entry.GetRef(&fOutputEntryRef);
break;
}
}
int32 argc = refCount + 3;
const char** argv = new const char* [argc + 1];
argv[0] = strdup("/bin/zip");
argv[1] = strdup("-ry");
argv[2] = strdup(archiveName.String());
for (int index = 0; index < refCount; index++) {
fThreadDataStore->FindRef("refs", index, &ref);
if (gotDirRef || sameFolder) {
argv[3 + index] = strdup(ref.name);
} else {
BPath path(&ref);
BString file = path.Path();
argv[3 + index] = strdup(path.Path());
}
}
argv[argc] = NULL;
fZipProcess = _PipeCommand(argc, argv, fStdIn, fStdOut, fStdErr);
delete [] argv;
if (fZipProcess < 0)
return fZipProcess;
resume_thread(fZipProcess);
fOutputFile = fdopen(fStdOut, "r");
if (fOutputFile == NULL)
return errno;
_SendMessageToWindow(ZIPPO_TASK_DESCRIPTION, "archive_filename",
archiveName.String());
_SendMessageToWindow(ZIPPO_LINE_OF_STDOUT, "zip_output",
B_TRANSLATE("Preparing to archive"));
return B_OK;
}
status_t
ZipperThread::ExecuteUnit()
{
char buffer[4096];
char* output = fgets(buffer, sizeof(buffer) - 1, fOutputFile);
if (output == NULL)
return EOF;
char* newLine = strrchr(output, '\n');
if (newLine != NULL)
*newLine = '\0';
if (!strncmp(" a", output, 3)) {
output[2] = 'A';
_SendMessageToWindow(ZIPPO_LINE_OF_STDOUT, "zip_output", output + 2);
} else if (!strncmp("up", output, 2)) {
output[0] = 'U';
_SendMessageToWindow(ZIPPO_LINE_OF_STDOUT, "zip_output", output);
} else {
_SendMessageToWindow(ZIPPO_LINE_OF_STDOUT, "zip_output", output);
}
return B_OK;
}
status_t
ZipperThread::ThreadShutdown()
{
close(fStdIn);
close(fStdOut);
close(fStdErr);
_SelectInTracker();
return B_OK;
}
void
ZipperThread::ThreadStartupFailed(status_t status)
{
fprintf(stderr, "ZipperThread::ThreadStartupFailed(): %s\n",
strerror(status));
_SendMessageToWindow(ZIPPO_THREAD_EXIT_ERROR);
Quit();
}
void
ZipperThread::ExecuteUnitFailed(status_t status)
{
if (status == EOF) {
fprintf(stderr, "ZipperThread::ExecuteUnitFailed(): EOF\n");
_SendMessageToWindow(ZIPPO_THREAD_EXIT);
} else {
fprintf(stderr, "ZipperThread::ExecuteUnitFailed(): %s\n",
strerror(status));
_SendMessageToWindow(ZIPPO_THREAD_EXIT_ERROR);
}
Quit();
}
void
ZipperThread::ThreadShutdownFailed(status_t status)
{
fprintf(stderr, "ZipperThread::ThreadShutdownFailed(): %s\n",
strerror(status));
}
void
ZipperThread::_MakeShellSafe(BString* string)
{
string->CharacterEscape("\"$`", '\\');
string->Prepend("\"");
string->Append("\"");
}
thread_id
ZipperThread::_PipeCommand(int argc, const char** argv, int& in, int& out,
int& err, const char** envp)
{
static BLocker lock;
if (lock.Lock()) {
thread_id thread;
int oldIn = dup(STDIN_FILENO);
int oldOut = dup(STDOUT_FILENO);
int oldErr = dup(STDERR_FILENO);
int inPipe[2], outPipe[2], errPipe[2];
if (pipe(inPipe) < 0)
goto err1;
if (pipe(outPipe) < 0)
goto err2;
if (pipe(errPipe) < 0)
goto err3;
errno = 0;
dup2(inPipe[0], STDIN_FILENO);
close(inPipe[0]);
dup2(outPipe[1], STDOUT_FILENO);
close(outPipe[1]);
dup2(errPipe[1], STDERR_FILENO);
close(errPipe[1]);
if (errno == 0) {
in = inPipe[1];
out = outPipe[0];
err = errPipe[0];
thread = load_image(argc, argv, envp);
} else {
thread = errno;
}
dup2(oldIn, STDIN_FILENO);
close(oldIn);
dup2(oldOut, STDOUT_FILENO);
close(oldOut);
dup2(oldErr, STDERR_FILENO);
close(oldErr);
lock.Unlock();
return thread;
err3:
close(outPipe[0]);
close(outPipe[1]);
err2:
close(inPipe[0]);
close(inPipe[1]);
err1:
close(oldIn);
close(oldOut);
close(oldErr);
lock.Unlock();
return errno;
} else {
return B_ERROR;
}
}
void
ZipperThread::_SendMessageToWindow(uint32 what, const char* name,
const char* value)
{
BMessage msg(what);
if (name != NULL && value != NULL)
msg.AddString(name, value);
fWindowMessenger.SendMessage(&msg);
}
status_t
ZipperThread::SuspendExternalZip()
{
thread_info info;
status_t status = get_thread_info(fZipProcess, &info);
if (status == B_OK && !strcmp(info.name, "zip"))
return suspend_thread(fZipProcess);
return status;
}
status_t
ZipperThread::ResumeExternalZip()
{
thread_info info;
status_t status = get_thread_info(fZipProcess, &info);
if (status == B_OK && !strcmp(info.name, "zip"))
return resume_thread(fZipProcess);
return status;
}
status_t
ZipperThread::InterruptExternalZip()
{
thread_info info;
status_t status = get_thread_info(fZipProcess, &info);
if (status == B_OK && !strcmp(info.name, "zip")) {
status = send_signal(fZipProcess, SIGINT);
WaitOnExternalZip();
return status;
}
return status;
}
status_t
ZipperThread::WaitOnExternalZip()
{
thread_info info;
status_t status = get_thread_info(fZipProcess, &info);
if (status == B_OK && !strcmp(info.name, "zip"))
return wait_for_thread(fZipProcess, &status);
return status;
}
status_t
ZipperThread::_SelectInTracker()
{
entry_ref parentRef;
BEntry entry(&fOutputEntryRef);
if (!entry.Exists())
return B_ENTRY_NOT_FOUND;
entry.GetParent(&entry);
entry.GetRef(&parentRef);
BMessenger trackerMessenger(kTrackerSignature);
if (!trackerMessenger.IsValid())
return B_ERROR;
BMessage request, reply;
request.what = B_REFS_RECEIVED;
request.AddRef("refs", &parentRef);
status_t status = trackerMessenger.SendMessage(&request, &reply);
if (status != B_OK)
return status;
snooze(300000);
request.MakeEmpty();
request.what = BPrivate::kSelect;
request.AddRef("refs", &fOutputEntryRef);
reply.MakeEmpty();
return trackerMessenger.SendMessage(&request, &reply);
}