/* * 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 */ #include "ZipperThread.h" #include #include #include #include #include #include #include #include "ZipOMaticMisc.h" #include "ZipOMaticWindow.h" 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); // leak? // prevents bug with B_SIMPLE_DATA // (drag&drop messages) } ZipperThread::~ZipperThread() { } status_t ZipperThread::ThreadStartup() { BString archiveName = "Archive.zip"; // do all refs have the same parent dir? 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) return status; for (int index = 0; index < refCount; index++) { fThreadDataStore->FindRef("refs", index, &ref); if (index > 0) { BEntry entry(&ref); if (entry.IsSymLink()) { entry.SetTo(&ref, true); entry_ref target; entry.GetRef(&target); if (lastRef.directory != target.directory) { sameFolder = false; break; } } else if (lastRef.directory != ref.directory) { sameFolder = false; break; } } lastRef = ref; } // change active dir 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()); } // archive filename if (refCount == 1) { archiveName = lastRef.name; archiveName += ".zip"; } 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()); // files to zip for (int index = 0; index < refCount; index++) { fThreadDataStore->FindRef("refs", index, &ref); if (sameFolder) { // just the file name argv[3 + index] = strdup(ref.name); } else { // full path 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; archiveName.Prepend("Creating archive: "); _SendMessageToWindow(ZIPPO_TASK_DESCRIPTION, "archive_filename", archiveName.String()); _SendMessageToWindow(ZIPPO_LINE_OF_STDOUT, "zip_output", "Preparing to archive"); return B_OK; } status_t ZipperThread::ExecuteUnit() { // read output from /bin/zip // send it to window 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); return B_OK; } void ZipperThread::ThreadStartupFailed(status_t status) { ErrorMessage("ZipperThread::ThreadStartupFailed() \n", status); Quit(); } void ZipperThread::ExecuteUnitFailed(status_t status) { ErrorMessage("ZipperThread::ExecuteUnitFailed() \n", status); if (status == EOF) { // thread has finished, been quit or killed, we don't know _SendMessageToWindow(ZIPPO_THREAD_EXIT); } else { // explicit error - communicate error to Window _SendMessageToWindow(ZIPPO_THREAD_EXIT_ERROR); } Quit(); } void ZipperThread::ThreadShutdownFailed(status_t status) { ErrorMessage("ZipperThread::ThreadShutdownFailed() \n", 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) { // This function was originally written by Peter Folk // and published in the BeDevTalk FAQ // http://www.abisoft.com/faq/BeDevTalk_FAQ.html#FAQ-209 thread_id thread; // Save current FDs int oldIn = dup(STDIN_FILENO); int oldOut = dup(STDOUT_FILENO); int oldErr = dup(STDERR_FILENO); int inPipe[2], outPipe[2], errPipe[2]; // Create new pipe FDs as stdin, stdout, stderr if (pipe(inPipe) < 0) goto err1; if (pipe(outPipe) < 0) goto err2; if (pipe(errPipe) < 0) goto err3; errno = 0; // replace old stdin/stderr/stdout 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]; // Write to in, appears on cmd's stdin out = outPipe[0]; // Read from out, taken from cmd's stdout err = errPipe[0]; // Read from err, taken from cmd's stderr // execute command thread = load_image(argc, argv, envp); } else { thread = errno; } // Restore old FDs dup2(oldIn, STDIN_FILENO); close(oldIn); dup2(oldOut, STDOUT_FILENO); close(oldOut); dup2(oldErr, STDERR_FILENO); close(oldErr); return thread; err3: close(outPipe[0]); close(outPipe[1]); err2: close(inPipe[0]); close(inPipe[1]); err1: close(oldIn); close(oldOut); close(oldErr); return errno; } 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 = B_OK; 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; }