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