1 /* 2 Open Tracker License 3 4 Terms and Conditions 5 6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy of 9 this software and associated documentation files (the "Software"), to deal in 10 the Software without restriction, including without limitation the rights to 11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 of the Software, and to permit persons to whom the Software is furnished to do 13 so, subject to the following conditions: 14 15 The above copyright notice and this permission notice applies to all licensees 16 and shall be included in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 22 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN 23 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25 Except as contained in this notice, the name of Be Incorporated shall not be 26 used in advertising or otherwise to promote the sale, use or other dealings in 27 this Software without prior written authorization from Be Incorporated. 28 29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered 30 trademarks of Be Incorporated in the United States and other countries. Other 31 brand product names are registered trademarks or trademarks of their 32 respective holders. All rights reserved. 33 */ 34 35 // Tracker file system calls. 36 37 // Note - APIs/code in FSUtils.h and FSUtils.cpp is slated for a major cleanup 38 // -- in other words, you will find a lot of ugly cruft in here 39 40 // ToDo: 41 // Move most of preflight error checks to the Model level and only keep those 42 // that have to do with size, reading/writing and name collisions. 43 // Get rid of all the BList based APIs, use BObjectLists. 44 // Clean up the error handling, push most of the user interaction out of the 45 // low level FS calls. 46 47 #include <ctype.h> 48 #include <errno.h> 49 #include <string.h> 50 #include <unistd.h> 51 52 #include <Alert.h> 53 #include <Application.h> 54 #include <Catalog.h> 55 #include <Debug.h> 56 #include <Directory.h> 57 #include <Entry.h> 58 #include <FindDirectory.h> 59 #include <Locale.h> 60 #include <NodeInfo.h> 61 #include <Path.h> 62 #include <Roster.h> 63 #include <Screen.h> 64 #include <String.h> 65 #include <SymLink.h> 66 #include <Volume.h> 67 #include <VolumeRoster.h> 68 69 #include <fs_attr.h> 70 #include <fs_info.h> 71 72 #include "Attributes.h" 73 #include "Bitmaps.h" 74 #include "Commands.h" 75 #include "FSUndoRedo.h" 76 #include "FSUtils.h" 77 #include "InfoWindow.h" 78 #include "MimeTypes.h" 79 #include "Model.h" 80 #include "OverrideAlert.h" 81 #include "StatusWindow.h" 82 #include "Thread.h" 83 #include "Tracker.h" 84 #include "TrackerSettings.h" 85 #include "Utilities.h" 86 87 88 enum { 89 kUserCanceled = B_ERRORS_END + 1, 90 kCopyCanceled = kUserCanceled, 91 kTrashCanceled 92 }; 93 94 enum ConflictCheckResult { 95 kCanceled = kUserCanceled, 96 kPrompt, 97 kReplace, 98 kReplaceAll, 99 kNoConflicts 100 }; 101 102 namespace BPrivate { 103 104 #undef B_TRANSLATE_CONTEXT 105 #define B_TRANSLATE_CONTEXT "FSUtils" 106 107 static status_t FSDeleteFolder(BEntry *, CopyLoopControl *, bool updateStatus, 108 bool deleteTopDir = true, bool upateFileNameInStatus = false); 109 static status_t MoveEntryToTrash(BEntry *, BPoint *, Undo &undo); 110 static void LowLevelCopy(BEntry *, StatStruct *, BDirectory *, char *destName, 111 CopyLoopControl *, BPoint *); 112 status_t DuplicateTask(BObjectList<entry_ref> *srcList); 113 static status_t MoveTask(BObjectList<entry_ref> *, BEntry *, BList *, uint32); 114 static status_t _DeleteTask(BObjectList<entry_ref> *, bool); 115 static status_t _RestoreTask(BObjectList<entry_ref> *); 116 status_t CalcItemsAndSize(CopyLoopControl* loopControl, 117 BObjectList<entry_ref> *refList, size_t blockSize, int32 *totalCount, 118 off_t *totalSize); 119 status_t MoveItem(BEntry *entry, BDirectory *destDir, BPoint *loc, 120 uint32 moveMode, const char *newName, Undo &undo, 121 CopyLoopControl* loopControl); 122 ConflictCheckResult PreFlightNameCheck(BObjectList<entry_ref> *srcList, 123 const BDirectory *destDir, int32 *collisionCount, uint32 moveMode); 124 status_t CheckName(uint32 moveMode, const BEntry *srcEntry, 125 const BDirectory *destDir, bool multipleCollisions, ConflictCheckResult &); 126 void CopyAttributes(CopyLoopControl *control, BNode *srcNode, BNode* destNode, void *buffer, 127 size_t bufsize); 128 void CopyPoseLocation(BNode *src, BNode *dest); 129 bool DirectoryMatchesOrContains(const BEntry *, directory_which); 130 bool DirectoryMatchesOrContains(const BEntry *, const char *additionalPath, 131 directory_which); 132 bool DirectoryMatches(const BEntry *, directory_which); 133 bool DirectoryMatches(const BEntry *, const char *additionalPath, 134 directory_which); 135 136 status_t empty_trash(void *); 137 138 #ifdef __HAIKU__ 139 #define OS_NAME "Haiku" 140 #else 141 #define OS_NAME "BeOS" 142 #endif 143 144 145 static const char* kDeleteConfirmationStr = 146 B_TRANSLATE_MARK("Are you sure you want to delete the " 147 "selected item(s)? This operation cannot be reverted."); 148 149 static const char* kReplaceStr = 150 B_TRANSLATE_MARK("You are trying to replace the item:\n" 151 "\t%name%dest\n" 152 "with:\n" 153 "\t%name%src\n\n" 154 "Would you like to replace it with the one you are %movemode?"); 155 156 static const char* kDirectoryReplaceStr = 157 B_TRANSLATE_MARK("An item named \"%name\" already exists in " 158 "this folder, and may contain\nitems with the same names. Would you like " 159 "to replace them with those contained in the folder you are %verb?"); 160 161 static const char* kSymLinkReplaceStr = 162 B_TRANSLATE_MARK("An item named \"%name\" already exists in this " 163 "folder. Would you like to replace it with the symbolic link you are " 164 "creating?"); 165 166 static const char* kNoFreeSpace = 167 B_TRANSLATE_MARK("Sorry, there is not enough free space on the " 168 "destination volume to copy the selection."); 169 170 static const char* kFileErrorString = 171 B_TRANSLATE_MARK("Error copying file \"%name\":\n\t%error\n\n" 172 "Would you like to continue?"); 173 174 static const char* kFolderErrorString = 175 B_TRANSLATE_MARK("Error copying folder \"%name\":\n\t%error\n\n" 176 "Would you like to continue?"); 177 178 static const char* kFileDeleteErrorString = 179 B_TRANSLATE_MARK("There was an error deleting \"%name\"" 180 ":\n\t%error"); 181 182 static const char* kReplaceManyStr = 183 B_TRANSLATE_MARK("Some items already exist in this folder with " 184 "the same names as the items you are %verb.\n \nWould you like to replace " 185 "them with the ones you are %verb or be prompted for each one?"); 186 187 static const char* kFindAlternativeStr = 188 B_TRANSLATE_MARK("Would you like to find some other suitable application?"); 189 190 static const char *kFindApplicationStr = 191 B_TRANSLATE_MARK("Would you like to find a suitable application " 192 "to open the file?"); 193 194 // Skip these attributes when copying in Tracker 195 const char *kSkipAttributes[] = { 196 kAttrPoseInfo, 197 NULL 198 }; 199 200 201 // #pragma mark - 202 203 204 CopyLoopControl::~CopyLoopControl() 205 { 206 } 207 208 209 void 210 CopyLoopControl::Init(uint32 jobKind) 211 { 212 } 213 214 215 void 216 CopyLoopControl::Init(int32 totalItems, off_t totalSize, 217 const entry_ref* destDir, bool showCount) 218 { 219 } 220 221 222 bool 223 CopyLoopControl::FileError(const char* message, const char* name, 224 status_t error, bool allowContinue) 225 { 226 return false; 227 } 228 229 230 void 231 CopyLoopControl::UpdateStatus(const char* name, const entry_ref& ref, 232 int32 count, bool optional) 233 { 234 } 235 236 237 bool 238 CopyLoopControl::CheckUserCanceled() 239 { 240 return false; 241 } 242 243 244 CopyLoopControl::OverwriteMode 245 CopyLoopControl::OverwriteOnConflict(const BEntry* srcEntry, 246 const char* destName, const BDirectory* destDir, bool srcIsDir, 247 bool dstIsDir) 248 { 249 return kReplace; 250 } 251 252 253 bool 254 CopyLoopControl::SkipEntry(const BEntry*, bool) 255 { 256 // Tracker makes no exceptions 257 return false; 258 } 259 260 261 void 262 CopyLoopControl::ChecksumChunk(const char*, size_t) 263 { 264 } 265 266 267 bool 268 CopyLoopControl::ChecksumFile(const entry_ref*) 269 { 270 return true; 271 } 272 273 274 bool 275 CopyLoopControl::SkipAttribute(const char*) 276 { 277 return false; 278 } 279 280 281 bool 282 CopyLoopControl::PreserveAttribute(const char*) 283 { 284 return false; 285 } 286 287 288 // #pragma mark - 289 290 291 TrackerCopyLoopControl::TrackerCopyLoopControl() 292 : 293 fThread(find_thread(NULL)), 294 fSourceList(NULL) 295 { 296 } 297 298 299 TrackerCopyLoopControl::TrackerCopyLoopControl(uint32 jobKind) 300 : 301 fThread(find_thread(NULL)), 302 fSourceList(NULL) 303 { 304 Init(jobKind); 305 } 306 307 308 TrackerCopyLoopControl::TrackerCopyLoopControl(int32 totalItems, 309 off_t totalSize) 310 : 311 fThread(find_thread(NULL)), 312 fSourceList(NULL) 313 { 314 Init(totalItems, totalSize); 315 } 316 317 318 TrackerCopyLoopControl::~TrackerCopyLoopControl() 319 { 320 if (gStatusWindow != NULL) 321 gStatusWindow->RemoveStatusItem(fThread); 322 } 323 324 325 void 326 TrackerCopyLoopControl::Init(uint32 jobKind) 327 { 328 if (gStatusWindow != NULL) 329 gStatusWindow->CreateStatusItem(fThread, (StatusWindowState)jobKind); 330 } 331 332 333 void 334 TrackerCopyLoopControl::Init(int32 totalItems, off_t totalSize, 335 const entry_ref* destDir, bool showCount) 336 { 337 if (gStatusWindow != NULL) { 338 gStatusWindow->InitStatusItem(fThread, totalItems, totalSize, 339 destDir, showCount); 340 } 341 } 342 343 344 bool 345 TrackerCopyLoopControl::FileError(const char *message, const char *name, 346 status_t error, bool allowContinue) 347 { 348 BString buffer(message); 349 buffer.ReplaceFirst("%name", name); 350 buffer.ReplaceFirst("%error", strerror(error)); 351 352 if (allowContinue) { 353 BAlert* alert = new BAlert("", buffer.String(), B_TRANSLATE("Cancel"), 354 B_TRANSLATE("OK"), 0, B_WIDTH_AS_USUAL, B_STOP_ALERT); 355 alert->SetShortcut(0, B_ESCAPE); 356 return alert->Go() != 0; 357 } 358 359 BAlert* alert = new BAlert("", buffer.String(), B_TRANSLATE("Cancel"), 0, 0, 360 B_WIDTH_AS_USUAL, B_STOP_ALERT); 361 alert->SetShortcut(0, B_ESCAPE); 362 alert->Go(); 363 return false; 364 } 365 366 367 void 368 TrackerCopyLoopControl::UpdateStatus(const char *name, const entry_ref&, 369 int32 count, bool optional) 370 { 371 if (gStatusWindow != NULL) 372 gStatusWindow->UpdateStatus(fThread, name, count, optional); 373 } 374 375 376 bool 377 TrackerCopyLoopControl::CheckUserCanceled() 378 { 379 if (gStatusWindow == NULL) 380 return false; 381 382 if (gStatusWindow->CheckCanceledOrPaused(fThread)) 383 return true; 384 385 if (fSourceList != NULL) { 386 // TODO: Check if the user dropped additional files onto this job. 387 // printf("%p->CheckUserCanceled()\n", this); 388 } 389 390 return false; 391 } 392 393 394 bool 395 TrackerCopyLoopControl::SkipAttribute(const char *attributeName) 396 { 397 for (const char **skipAttribute = kSkipAttributes; *skipAttribute; 398 skipAttribute++) { 399 if (strcmp(*skipAttribute, attributeName) == 0) 400 return true; 401 } 402 403 return false; 404 } 405 406 407 void 408 TrackerCopyLoopControl::SetSourceList(EntryList* list) 409 { 410 fSourceList = list; 411 } 412 413 414 // #pragma mark - 415 416 417 static BNode * 418 GetWritableNode(BEntry *entry, StatStruct *statBuf = 0) 419 { 420 // utility call that works around the problem with BNodes not being 421 // universally writeable 422 // BNodes created on files will fail to WriteAttr because they do not 423 // have the right r/w permissions 424 425 StatStruct localStatbuf; 426 427 if (!statBuf) { 428 statBuf = &localStatbuf; 429 if (entry->GetStat(statBuf) != B_OK) 430 return 0; 431 } 432 433 if (S_ISREG(statBuf->st_mode)) 434 return new BFile(entry, O_RDWR); 435 436 return new BNode(entry); 437 } 438 439 440 bool 441 CheckDevicesEqual(const entry_ref *srcRef, const Model *targetModel) 442 { 443 BDirectory destDir (targetModel->EntryRef()); 444 struct stat deststat; 445 destDir.GetStat(&deststat); 446 447 return srcRef->device == deststat.st_dev; 448 } 449 450 451 status_t 452 FSSetPoseLocation(ino_t destDirInode, BNode *destNode, BPoint point) 453 { 454 PoseInfo poseInfo; 455 poseInfo.fInvisible = false; 456 poseInfo.fInitedDirectory = destDirInode; 457 poseInfo.fLocation = point; 458 459 status_t result = destNode->WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, 460 &poseInfo, sizeof(poseInfo)); 461 462 if (result == sizeof(poseInfo)) 463 return B_OK; 464 465 return result; 466 } 467 468 469 status_t 470 FSSetPoseLocation(BEntry *entry, BPoint point) 471 { 472 BNode node(entry); 473 status_t result = node.InitCheck(); 474 if (result != B_OK) 475 return result; 476 477 BDirectory parent; 478 result = entry->GetParent(&parent); 479 if (result != B_OK) 480 return result; 481 482 node_ref destNodeRef; 483 result = parent.GetNodeRef(&destNodeRef); 484 if (result != B_OK) 485 return result; 486 487 return FSSetPoseLocation(destNodeRef.node, &node, point); 488 } 489 490 491 bool 492 FSGetPoseLocation(const BNode *node, BPoint *point) 493 { 494 PoseInfo poseInfo; 495 if (ReadAttr(node, kAttrPoseInfo, kAttrPoseInfoForeign, 496 B_RAW_TYPE, 0, &poseInfo, sizeof(poseInfo), &PoseInfo::EndianSwap) 497 == kReadAttrFailed) 498 return false; 499 500 if (poseInfo.fInitedDirectory == -1LL) 501 return false; 502 503 *point = poseInfo.fLocation; 504 505 return true; 506 } 507 508 509 static void 510 SetUpPoseLocation(ino_t sourceParentIno, ino_t destParentIno, 511 const BNode *sourceNode, BNode *destNode, BPoint *loc) 512 { 513 BPoint point; 514 if (!loc 515 // we don't have a position yet 516 && sourceParentIno != destParentIno 517 // we aren't copying into the same directory 518 && FSGetPoseLocation(sourceNode, &point)) 519 // the original has a valid inited location 520 loc = &point; 521 // copy the originals location 522 523 if (loc && loc != (BPoint *)-1) { 524 // loc of -1 is used when copying/moving into a window in list mode 525 // where copying positions would not work 526 // ToSo: 527 // should push all this logic to upper levels 528 FSSetPoseLocation(destParentIno, destNode, *loc); 529 } 530 } 531 532 533 void 534 FSMoveToFolder(BObjectList<entry_ref> *srcList, BEntry *destEntry, 535 uint32 moveMode, BList *pointList) 536 { 537 if (srcList->IsEmpty()) { 538 delete srcList; 539 delete pointList; 540 delete destEntry; 541 return; 542 } 543 544 LaunchInNewThread("MoveTask", B_NORMAL_PRIORITY, MoveTask, srcList, 545 destEntry, pointList, moveMode); 546 } 547 548 549 void 550 FSDelete(entry_ref *ref, bool async, bool confirm) 551 { 552 BObjectList<entry_ref> *list = new BObjectList<entry_ref>(1, true); 553 list->AddItem(ref); 554 FSDeleteRefList(list, async, confirm); 555 } 556 557 558 void 559 FSDeleteRefList(BObjectList<entry_ref> *list, bool async, bool confirm) 560 { 561 if (async) { 562 LaunchInNewThread("DeleteTask", B_NORMAL_PRIORITY, _DeleteTask, list, 563 confirm); 564 } else 565 _DeleteTask(list, confirm); 566 } 567 568 569 void 570 FSRestoreRefList(BObjectList<entry_ref> *list, bool async) 571 { 572 if (async) { 573 LaunchInNewThread("RestoreTask", B_NORMAL_PRIORITY, _RestoreTask, 574 list); 575 } else 576 _RestoreTask(list); 577 } 578 579 580 void 581 FSMoveToTrash(BObjectList<entry_ref> *srcList, BList *pointList, bool async) 582 { 583 if (srcList->IsEmpty()) { 584 delete srcList; 585 delete pointList; 586 return; 587 } 588 589 if (async) 590 LaunchInNewThread("MoveTask", B_NORMAL_PRIORITY, MoveTask, srcList, 591 (BEntry *)0, pointList, kMoveSelectionTo); 592 else 593 MoveTask(srcList, 0, pointList, kMoveSelectionTo); 594 } 595 596 597 static bool 598 IsDisksWindowIcon(BEntry *entry) 599 { 600 BPath path; 601 if (entry->InitCheck() != B_OK || entry->GetPath(&path) != B_OK) 602 return false; 603 604 return strcmp(path.Path(), "/") == 0; 605 } 606 607 enum { 608 kNotConfirmed, 609 kConfirmedHomeMove, 610 kConfirmedAll 611 }; 612 613 614 bool 615 ConfirmChangeIfWellKnownDirectory(const BEntry *entry, const char *action, 616 bool dontAsk, int32 *confirmedAlready) 617 { 618 // Don't let the user casually move/change important files/folders 619 // 620 // This is a cheap replacement for having a real UID support turned 621 // on and not running as root all the time 622 623 if (confirmedAlready && *confirmedAlready == kConfirmedAll) 624 return true; 625 626 if (!DirectoryMatchesOrContains(entry, B_SYSTEM_DIRECTORY) 627 && !DirectoryMatchesOrContains(entry, B_USER_DIRECTORY)) 628 // quick way out 629 return true; 630 631 BString warning; 632 bool requireOverride = true; 633 634 if (DirectoryMatchesOrContains(entry, B_BEOS_DIRECTORY)) { 635 warning.SetTo( 636 B_TRANSLATE("If you %action the system folder or its contents, you " 637 "won't be able to boot " OS_NAME "! Are you sure you want to do " 638 "this? To %action the system folder or its contents anyway, hold down " 639 "the Shift key and click \"Do it\".")); 640 } else if (DirectoryMatches(entry, B_USER_DIRECTORY)) { 641 warning .SetTo( 642 B_TRANSLATE("If you %action the home folder, " OS_NAME " may not " 643 "behave properly! Are you sure you want to do this? " 644 "To %action the home anyway, click \"Do it\".")); 645 requireOverride = false; 646 } else if (DirectoryMatchesOrContains(entry, B_USER_CONFIG_DIRECTORY) 647 || DirectoryMatchesOrContains(entry, B_COMMON_SETTINGS_DIRECTORY)) { 648 649 if (DirectoryMatchesOrContains(entry, "beos_mime", 650 B_USER_SETTINGS_DIRECTORY) 651 || DirectoryMatchesOrContains(entry, "beos_mime", 652 B_COMMON_SETTINGS_DIRECTORY)) { 653 warning.SetTo( 654 B_TRANSLATE("If you %action the mime settings, " OS_NAME 655 " may not behave properly! Are you sure you want to do this? " 656 "To %action the mime settings anyway, click \"Do it\".")); 657 requireOverride = false; 658 } else if (DirectoryMatches(entry, B_USER_CONFIG_DIRECTORY)) { 659 warning.SetTo( 660 B_TRANSLATE("If you %action the config folder, " OS_NAME 661 " may not behave properly! Are you sure you want to do this? " 662 "To %action the config folder anyway, click \"Do it\".")); 663 requireOverride = false; 664 } else if (DirectoryMatches(entry, B_USER_SETTINGS_DIRECTORY) 665 || DirectoryMatches(entry, B_COMMON_SETTINGS_DIRECTORY)) { 666 warning.SetTo( 667 B_TRANSLATE("If you %action the settings folder, " OS_NAME 668 " may not behave properly! Are you sure you want to do this? " 669 "To %action the settings folder anyway, click \"Do it\".")); 670 requireOverride = false; 671 } 672 } 673 674 if (!warning.Length()) 675 return true; 676 677 if (dontAsk) 678 return false; 679 680 if (confirmedAlready && *confirmedAlready == kConfirmedHomeMove 681 && !requireOverride) 682 // we already warned about moving home this time around 683 return true; 684 685 warning.ReplaceAll("%action", action); 686 687 if ((new OverrideAlert("", warning.String(), B_TRANSLATE("Do it"), 688 (requireOverride ? B_SHIFT_KEY : 0), B_TRANSLATE("Cancel"), 0, NULL, 689 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go() == 1) { 690 if (confirmedAlready) 691 *confirmedAlready = kNotConfirmed; 692 return false; 693 } 694 695 if (confirmedAlready) { 696 if (!requireOverride) 697 *confirmedAlready = kConfirmedHomeMove; 698 else 699 *confirmedAlready = kConfirmedAll; 700 } 701 702 return true; 703 } 704 705 706 static status_t 707 InitCopy(CopyLoopControl* loopControl, uint32 moveMode, 708 BObjectList<entry_ref> *srcList, BVolume *dstVol, BDirectory *destDir, 709 entry_ref *destRef, bool preflightNameCheck, bool needSizeCalculation, 710 int32 *collisionCount, ConflictCheckResult *preflightResult) 711 { 712 if (dstVol->IsReadOnly()) { 713 BAlert* alert = new BAlert("", 714 B_TRANSLATE("You can't move or copy items to read-only volumes."), 715 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 716 alert->SetShortcut(0, B_ESCAPE); 717 alert->Go(); 718 return B_ERROR; 719 } 720 721 int32 numItems = srcList->CountItems(); 722 int32 askOnceOnly = kNotConfirmed; 723 for (int32 index = 0; index < numItems; index++) { 724 // we could check for this while iterating through items in each of 725 // the copy loops, except it takes forever to call CalcItemsAndSize 726 BEntry entry((entry_ref *)srcList->ItemAt(index)); 727 if (IsDisksWindowIcon(&entry)) { 728 BString errorStr; 729 if (moveMode == kCreateLink) { 730 errorStr.SetTo( 731 B_TRANSLATE("You cannot create a link to the root " 732 "directory.")); 733 } else { 734 errorStr.SetTo( 735 B_TRANSLATE("You cannot copy or move the root directory.")); 736 } 737 738 BAlert* alert = new BAlert("", errorStr.String(), 739 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 740 alert->SetShortcut(0, B_ESCAPE); 741 alert->Go(); 742 return B_ERROR; 743 } 744 if (moveMode == kMoveSelectionTo 745 && !ConfirmChangeIfWellKnownDirectory(&entry, B_TRANSLATE("move"), 746 false, &askOnceOnly)) { 747 return B_ERROR; 748 } 749 } 750 751 if (preflightNameCheck) { 752 ASSERT(collisionCount); 753 ASSERT(preflightResult); 754 755 *preflightResult = kPrompt; 756 *collisionCount = 0; 757 758 *preflightResult = PreFlightNameCheck(srcList, destDir, collisionCount, 759 moveMode); 760 if (*preflightResult == kCanceled) // user canceled 761 return B_ERROR; 762 } 763 764 // set up the status display 765 switch (moveMode) { 766 case kCopySelectionTo: 767 case kDuplicateSelection: 768 case kMoveSelectionTo: 769 { 770 loopControl->Init(moveMode == kMoveSelectionTo ? kMoveState 771 : kCopyState); 772 773 int32 totalItems = 0; 774 off_t totalSize = 0; 775 if (needSizeCalculation) { 776 if (CalcItemsAndSize(loopControl, srcList, 777 dstVol->BlockSize(), &totalItems, &totalSize) != B_OK) { 778 return B_ERROR; 779 } 780 781 // check for free space before starting copy 782 if ((totalSize + (4 * kKBSize)) >= dstVol->FreeBytes()) { 783 BAlert* alert = new BAlert("", 784 B_TRANSLATE_NOCOLLECT(kNoFreeSpace), 785 B_TRANSLATE("Cancel"), 786 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 787 alert->SetShortcut(0, B_ESCAPE); 788 alert->Go(); 789 return B_ERROR; 790 } 791 } 792 793 loopControl->Init(totalItems, totalSize, destRef); 794 break; 795 } 796 797 case kCreateLink: 798 if (numItems > 10) { 799 // this will be fast, only put up status if lots of items 800 // moved, links created 801 loopControl->Init(kCreateLinkState); 802 loopControl->Init(numItems, numItems, destRef); 803 } 804 break; 805 } 806 return B_OK; 807 } 808 809 810 // ToDo: 811 // get rid of this cruft 812 bool 813 delete_ref(void *ref) 814 { 815 delete (entry_ref*)ref; 816 return false; 817 } 818 819 820 bool 821 delete_point(void *point) 822 { 823 delete (BPoint*)point; 824 return false; 825 } 826 827 828 static status_t 829 MoveTask(BObjectList<entry_ref> *srcList, BEntry *destEntry, BList *pointList, 830 uint32 moveMode) 831 { 832 ASSERT(!srcList->IsEmpty()); 833 834 // extract information from src, dest models 835 // ## note that we're assuming all items come from the same volume 836 // ## by looking only at FirstItem here which is not a good idea 837 dev_t srcVolumeDevice = srcList->FirstItem()->device; 838 dev_t destVolumeDevice = srcVolumeDevice; 839 840 StatStruct deststat; 841 BVolume volume(srcVolumeDevice); 842 entry_ref destRef; 843 const entry_ref *destRefToCheck = NULL; 844 845 bool destIsTrash = false; 846 BDirectory destDir; 847 BDirectory *destDirToCheck = NULL; 848 bool needPreflightNameCheck = false; 849 bool sourceIsReadOnly = volume.IsReadOnly(); 850 volume.Unset(); 851 852 bool fromUndo = FSIsUndoMoveMode(moveMode); 853 moveMode = FSMoveMode(moveMode); 854 855 // if we're not passed a destEntry then we are supposed to move to trash 856 if (destEntry != NULL) { 857 destEntry->GetRef(&destRef); 858 destRefToCheck = &destRef; 859 860 destDir.SetTo(destEntry); 861 destDir.GetStat(&deststat); 862 destDirToCheck = &destDir; 863 864 destVolumeDevice = deststat.st_dev; 865 destIsTrash = FSIsTrashDir(destEntry); 866 volume.SetTo(destVolumeDevice); 867 868 needPreflightNameCheck = true; 869 } else if (moveMode == kDuplicateSelection) 870 volume.SetTo(srcVolumeDevice); 871 else { 872 // move is to trash 873 destIsTrash = true; 874 875 FSGetTrashDir(&destDir, srcVolumeDevice); 876 volume.SetTo(srcVolumeDevice); 877 878 BEntry entry; 879 destDir.GetEntry(&entry); 880 destDirToCheck = &destDir; 881 882 entry.GetRef(&destRef); 883 destRefToCheck = &destRef; 884 } 885 886 // change the move mode if needed 887 if (moveMode == kCopySelectionTo && destIsTrash) { 888 // cannot copy to trash 889 moveMode = kMoveSelectionTo; 890 } 891 892 if (moveMode == kMoveSelectionTo && sourceIsReadOnly) 893 moveMode = kCopySelectionTo; 894 895 bool needSizeCalculation = true; 896 if ((moveMode == kMoveSelectionTo && srcVolumeDevice == destVolumeDevice) 897 || destIsTrash) { 898 needSizeCalculation = false; 899 } 900 901 // we need the undo object later on, so we create it no matter 902 // if we really need it or not (it's very lightweight) 903 MoveCopyUndo undo(srcList, destDir, pointList, moveMode); 904 if (fromUndo) 905 undo.Remove(); 906 907 TrackerCopyLoopControl loopControl; 908 909 ConflictCheckResult conflictCheckResult = kPrompt; 910 int32 collisionCount = 0; 911 // TODO: Status item is created in InitCopy(), but it would be kind of 912 // neat to move all that into TrackerCopyLoopControl 913 status_t result = InitCopy(&loopControl, moveMode, srcList, 914 &volume, destDirToCheck, &destRef, needPreflightNameCheck, 915 needSizeCalculation, &collisionCount, &conflictCheckResult); 916 917 loopControl.SetSourceList(srcList); 918 919 if (result == B_OK) { 920 for (int32 i = 0; i < srcList->CountItems(); i++) { 921 BPoint *loc = (BPoint *)-1; 922 // a loc of -1 forces autoplacement, rather than copying the 923 // position of the original node 924 // TODO: 925 // Clean this mess up! 926 // What could be a cleaner design is to pass along some kind 927 // "filter" object that post-processes poses, i.e. adds the 928 // location or other stuff. It should not be a job of the 929 // copy-engine. 930 931 entry_ref *srcRef = srcList->ItemAt(i); 932 933 if (moveMode == kDuplicateSelection) { 934 BEntry entry(srcRef); 935 entry.GetParent(&destDir); 936 destDir.GetStat(&deststat); 937 volume.SetTo(srcRef->device); 938 } 939 940 // handle case where item is dropped into folder it already lives 941 // in which could happen if dragging from a query window 942 if (moveMode != kCreateLink 943 && moveMode != kCreateRelativeLink 944 && moveMode != kDuplicateSelection 945 && !destIsTrash 946 && (srcRef->device == destRef.device 947 && srcRef->directory == deststat.st_ino)) 948 continue; 949 950 if (loopControl.CheckUserCanceled()) 951 break; 952 953 BEntry sourceEntry(srcRef); 954 if (sourceEntry.InitCheck() != B_OK) { 955 BString error(B_TRANSLATE("Error moving \"%name\".")); 956 error.ReplaceFirst("%name", srcRef->name); 957 BAlert* alert = new BAlert("", error.String(), 958 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, 959 B_WARNING_ALERT); 960 alert->SetShortcut(0, B_ESCAPE); 961 alert->Go(); 962 break; 963 } 964 965 // are we moving item to trash? 966 if (destIsTrash) { 967 if (pointList) 968 loc = (BPoint *)pointList->ItemAt(i); 969 970 result = MoveEntryToTrash(&sourceEntry, loc, undo); 971 if (result != B_OK) { 972 BString error(B_TRANSLATE("Error moving \"%name\" to Trash. (%error)")); 973 error.ReplaceFirst("%name", srcRef->name); 974 error.ReplaceFirst("%error", strerror(result)); 975 BAlert* alert = new BAlert("", error.String(), 976 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, 977 B_WARNING_ALERT); 978 alert->SetShortcut(0, B_ESCAPE); 979 alert->Go(); 980 break; 981 } 982 continue; 983 } 984 985 // resolve name collisions and hierarchy problems 986 if (CheckName(moveMode, &sourceEntry, &destDir, collisionCount > 1, 987 conflictCheckResult) != B_OK) { 988 // we will skip the current item, because we got a conflict 989 // and were asked to or because there was some conflict 990 991 // update the status because item got skipped and the status 992 // will not get updated by the move call 993 loopControl.UpdateStatus(srcRef->name, *srcRef, 1); 994 995 continue; 996 } 997 998 // get location to place this item 999 if (pointList && moveMode != kCopySelectionTo) { 1000 loc = (BPoint *)pointList->ItemAt(i); 1001 1002 BNode *src_node = GetWritableNode(&sourceEntry); 1003 if (src_node && src_node->InitCheck() == B_OK) { 1004 PoseInfo poseInfo; 1005 poseInfo.fInvisible = false; 1006 poseInfo.fInitedDirectory = deststat.st_ino; 1007 poseInfo.fLocation = *loc; 1008 src_node->WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, 1009 &poseInfo, sizeof(poseInfo)); 1010 } 1011 delete src_node; 1012 } 1013 1014 if (pointList) 1015 loc = (BPoint*)pointList->ItemAt(i); 1016 1017 result = MoveItem(&sourceEntry, &destDir, loc, moveMode, NULL, 1018 undo, &loopControl); 1019 if (result != B_OK) 1020 break; 1021 } 1022 } 1023 1024 // duplicates of srcList, destFolder were created - dispose them 1025 delete srcList; 1026 delete destEntry; 1027 1028 // delete file location list and all Points within 1029 if (pointList) { 1030 pointList->DoForEach(delete_point); 1031 delete pointList; 1032 } 1033 1034 return B_OK; 1035 } 1036 1037 class FailWithAlert { 1038 public: 1039 static void FailOnError(status_t error, const char *string, 1040 const char *name = NULL) 1041 { 1042 if (error != B_OK) 1043 throw FailWithAlert(error, string, name); 1044 } 1045 1046 FailWithAlert(status_t error, const char *string, const char *name) 1047 : fString(string), 1048 fName(name), 1049 fError(error) 1050 { 1051 } 1052 1053 const char *fString; 1054 const char *fName; 1055 status_t fError; 1056 }; 1057 1058 class MoveError { 1059 public: 1060 static void FailOnError(status_t error) 1061 { 1062 if (error != B_OK) 1063 throw MoveError(error); 1064 } 1065 1066 MoveError(status_t error) 1067 : fError(error) 1068 { } 1069 1070 status_t fError; 1071 }; 1072 1073 1074 void 1075 CopyFile(BEntry *srcFile, StatStruct *srcStat, BDirectory *destDir, 1076 CopyLoopControl *loopControl, BPoint *loc, bool makeOriginalName, 1077 Undo &undo) 1078 { 1079 if (loopControl->SkipEntry(srcFile, true)) 1080 return; 1081 1082 node_ref node; 1083 destDir->GetNodeRef(&node); 1084 BVolume volume(node.device); 1085 1086 // check for free space first 1087 if ((srcStat->st_size + kKBSize) >= volume.FreeBytes()) { 1088 loopControl->FileError(B_TRANSLATE_NOCOLLECT(kNoFreeSpace), "", 1089 B_DEVICE_FULL, false); 1090 throw (status_t)B_DEVICE_FULL; 1091 } 1092 1093 char destName[B_FILE_NAME_LENGTH]; 1094 srcFile->GetName(destName); 1095 entry_ref ref; 1096 srcFile->GetRef(&ref); 1097 1098 loopControl->UpdateStatus(destName, ref, 1024, true); 1099 1100 if (makeOriginalName) { 1101 BString suffix(" "); 1102 suffix << B_TRANSLATE_COMMENT("copy", "filename copy"), 1103 FSMakeOriginalName(destName, destDir, suffix.String()); 1104 undo.UpdateEntry(srcFile, destName); 1105 } 1106 1107 BEntry conflictingEntry; 1108 if (destDir->FindEntry(destName, &conflictingEntry) == B_OK) { 1109 switch (loopControl->OverwriteOnConflict(srcFile, destName, destDir, 1110 false, false)) { 1111 case TrackerCopyLoopControl::kSkip: 1112 // we are about to ignore this entire directory 1113 return; 1114 1115 case TrackerCopyLoopControl::kReplace: 1116 if (!conflictingEntry.IsDirectory()) { 1117 ThrowOnError(conflictingEntry.Remove()); 1118 break; 1119 } 1120 // fall through if not a directory 1121 case TrackerCopyLoopControl::kMerge: 1122 // This flag implies that the attributes should be kept 1123 // on the file. Just ignore it. 1124 break; 1125 } 1126 } 1127 1128 try { 1129 LowLevelCopy(srcFile, srcStat, destDir, destName, loopControl, loc); 1130 } catch (status_t err) { 1131 if (err == kCopyCanceled) 1132 throw (status_t)err; 1133 1134 if (err != B_OK) { 1135 if (!loopControl->FileError(B_TRANSLATE_NOCOLLECT(kFileErrorString), 1136 destName, err, true)) { 1137 throw (status_t)err; 1138 } else { 1139 // user selected continue in spite of error, update status bar 1140 loopControl->UpdateStatus(NULL, ref, (int32)srcStat->st_size); 1141 } 1142 } 1143 } 1144 } 1145 1146 1147 #ifdef _SILENTLY_CORRECT_FILE_NAMES 1148 static bool 1149 CreateFileSystemCompatibleName(const BDirectory *destDir, char *destName) 1150 { 1151 // Is it a FAT32 file system? (this is the only one we currently now about) 1152 1153 BEntry target; 1154 destDir->GetEntry(&target); 1155 entry_ref targetRef; 1156 fs_info info; 1157 if (target.GetRef(&targetRef) == B_OK 1158 && fs_stat_dev(targetRef.device, &info) == B_OK 1159 && !strcmp(info.fsh_name, "fat")) { 1160 bool wasInvalid = false; 1161 1162 // it's a FAT32 file system, now check the name 1163 1164 int32 length = strlen(destName) - 1; 1165 while (destName[length] == '.') { 1166 // invalid name, just cut off the dot at the end 1167 destName[length--] = '\0'; 1168 wasInvalid = true; 1169 } 1170 1171 char *invalid = destName; 1172 while ((invalid = strpbrk(invalid, "?<>\\:\"|*")) != NULL) { 1173 invalid[0] = '_'; 1174 wasInvalid = true; 1175 } 1176 1177 return wasInvalid; 1178 } 1179 1180 return false; 1181 } 1182 #endif 1183 1184 1185 static void 1186 LowLevelCopy(BEntry *srcEntry, StatStruct *srcStat, BDirectory *destDir, 1187 char *destName, CopyLoopControl *loopControl, BPoint *loc) 1188 { 1189 entry_ref ref; 1190 ThrowOnError(srcEntry->GetRef(&ref)); 1191 1192 if (S_ISLNK(srcStat->st_mode)) { 1193 // handle symbolic links 1194 BSymLink srcLink; 1195 BSymLink newLink; 1196 char linkpath[MAXPATHLEN]; 1197 1198 ThrowOnError(srcLink.SetTo(srcEntry)); 1199 ThrowIfNotSize(srcLink.ReadLink(linkpath, MAXPATHLEN-1)); 1200 1201 ThrowOnError(destDir->CreateSymLink(destName, linkpath, &newLink)); 1202 1203 node_ref destNodeRef; 1204 destDir->GetNodeRef(&destNodeRef); 1205 // copy or write new pose location as a first thing 1206 SetUpPoseLocation(ref.directory, destNodeRef.node, &srcLink, 1207 &newLink, loc); 1208 1209 BNodeInfo nodeInfo(&newLink); 1210 ThrowOnError(nodeInfo.SetType(B_LINK_MIMETYPE)); 1211 1212 newLink.SetPermissions(srcStat->st_mode); 1213 newLink.SetOwner(srcStat->st_uid); 1214 newLink.SetGroup(srcStat->st_gid); 1215 newLink.SetModificationTime(srcStat->st_mtime); 1216 newLink.SetCreationTime(srcStat->st_crtime); 1217 1218 return; 1219 } 1220 1221 BFile srcFile(srcEntry, O_RDONLY); 1222 ThrowOnInitCheckError(&srcFile); 1223 1224 const size_t kMinBufferSize = 1024 * 128; 1225 const size_t kMaxBufferSize = 1024 * 1024; 1226 1227 size_t bufsize = kMinBufferSize; 1228 if (bufsize < srcStat->st_size) { 1229 // File bigger than the buffer size: determine an optimal buffer size 1230 system_info sinfo; 1231 get_system_info(&sinfo); 1232 size_t freesize = static_cast<size_t>( 1233 (sinfo.max_pages - sinfo.used_pages) * B_PAGE_SIZE); 1234 bufsize = freesize / 4; // take 1/4 of RAM max 1235 bufsize -= bufsize % (16 * 1024); // Round to 16 KB boundaries 1236 if (bufsize < kMinBufferSize) // at least kMinBufferSize 1237 bufsize = kMinBufferSize; 1238 else if (bufsize > kMaxBufferSize) // no more than kMaxBufferSize 1239 bufsize = kMaxBufferSize; 1240 } 1241 1242 BFile destFile(destDir, destName, O_RDWR | O_CREAT); 1243 #ifdef _SILENTLY_CORRECT_FILE_NAMES 1244 if ((destFile.InitCheck() == B_BAD_VALUE 1245 || destFile.InitCheck() == B_NOT_ALLOWED) 1246 && CreateFileSystemCompatibleName(destDir, destName)) { 1247 destFile.SetTo(destDir, destName, B_CREATE_FILE | B_READ_WRITE); 1248 } 1249 #endif 1250 1251 ThrowOnInitCheckError(&destFile); 1252 1253 node_ref destNodeRef; 1254 destDir->GetNodeRef(&destNodeRef); 1255 // copy or write new pose location as a first thing 1256 SetUpPoseLocation(ref.directory, destNodeRef.node, &srcFile, 1257 &destFile, loc); 1258 1259 char *buffer = new char[bufsize]; 1260 try { 1261 // copy data portion of file 1262 while (true) { 1263 if (loopControl->CheckUserCanceled()) { 1264 // if copy was canceled, remove partial destination file 1265 destFile.Unset(); 1266 1267 BEntry destEntry; 1268 if (destDir->FindEntry(destName, &destEntry) == B_OK) 1269 destEntry.Remove(); 1270 1271 throw (status_t)kCopyCanceled; 1272 } 1273 1274 ASSERT(buffer); 1275 ssize_t bytes = srcFile.Read(buffer, bufsize); 1276 1277 if (bytes > 0) { 1278 ssize_t updateBytes = 0; 1279 if (bytes > 32 * 1024) { 1280 // when copying large chunks, update after read and after 1281 // write to get better update granularity 1282 updateBytes = bytes / 2; 1283 loopControl->UpdateStatus(NULL, ref, updateBytes, true); 1284 } 1285 1286 loopControl->ChecksumChunk(buffer, (size_t)bytes); 1287 1288 ssize_t result = destFile.Write(buffer, (size_t)bytes); 1289 if (result != bytes) 1290 throw (status_t)B_ERROR; 1291 1292 loopControl->UpdateStatus(NULL, ref, bytes - updateBytes, 1293 true); 1294 } else if (bytes < 0) { 1295 // read error 1296 throw (status_t)bytes; 1297 } else { 1298 // we are done 1299 break; 1300 } 1301 } 1302 1303 CopyAttributes(loopControl, &srcFile, &destFile, buffer, bufsize); 1304 } catch (...) { 1305 delete [] buffer; 1306 throw; 1307 } 1308 1309 destFile.SetPermissions(srcStat->st_mode); 1310 destFile.SetOwner(srcStat->st_uid); 1311 destFile.SetGroup(srcStat->st_gid); 1312 destFile.SetModificationTime(srcStat->st_mtime); 1313 destFile.SetCreationTime(srcStat->st_crtime); 1314 1315 delete [] buffer; 1316 1317 if (!loopControl->ChecksumFile(&ref)) { 1318 // File no good. Remove and quit. 1319 destFile.Unset(); 1320 1321 BEntry destEntry; 1322 if (destDir->FindEntry(destName, &destEntry) == B_OK) 1323 destEntry.Remove(); 1324 throw (status_t)kUserCanceled; 1325 } 1326 } 1327 1328 1329 void 1330 CopyAttributes(CopyLoopControl *control, BNode *srcNode, BNode *destNode, 1331 void *buffer, size_t bufsize) 1332 { 1333 // ToDo: 1334 // Add error checking 1335 // prior to coyping attributes, make sure indices are installed 1336 1337 // When calling CopyAttributes on files, have to make sure destNode 1338 // is a BFile opened R/W 1339 1340 srcNode->RewindAttrs(); 1341 char name[256]; 1342 while (srcNode->GetNextAttrName(name) == B_OK) { 1343 // Check to see if this attribute should be skipped. 1344 if (control->SkipAttribute(name)) 1345 continue; 1346 1347 attr_info info; 1348 if (srcNode->GetAttrInfo(name, &info) != B_OK) 1349 continue; 1350 1351 // Check to see if this attribute should be overwritten when it 1352 // already exists. 1353 if (control->PreserveAttribute(name)) { 1354 attr_info dest_info; 1355 if (destNode->GetAttrInfo(name, &dest_info) == B_OK) 1356 continue; 1357 } 1358 1359 // Special case for a size 0 attribute. It wouldn't be written at all 1360 // otherwise. 1361 if (info.size == 0) 1362 destNode->WriteAttr(name, info.type, 0, buffer, 0); 1363 1364 ssize_t bytes; 1365 ssize_t numToRead = (ssize_t)info.size; 1366 for (off_t offset = 0; numToRead > 0; offset += bytes) { 1367 size_t chunkSize = (size_t)numToRead; 1368 if (chunkSize > bufsize) 1369 chunkSize = bufsize; 1370 1371 bytes = srcNode->ReadAttr(name, info.type, offset, 1372 buffer, chunkSize); 1373 1374 if (bytes <= 0) 1375 break; 1376 1377 destNode->WriteAttr(name, info.type, offset, buffer, 1378 (size_t)bytes); 1379 1380 numToRead -= bytes; 1381 } 1382 } 1383 } 1384 1385 1386 static void 1387 CopyFolder(BEntry *srcEntry, BDirectory *destDir, CopyLoopControl *loopControl, 1388 BPoint *loc, bool makeOriginalName, Undo &undo, bool removeSource = false) 1389 { 1390 BDirectory newDir; 1391 BEntry entry; 1392 status_t err = B_OK; 1393 bool createDirectory = true; 1394 BEntry existingEntry; 1395 1396 if (loopControl->SkipEntry(srcEntry, false)) 1397 return; 1398 1399 entry_ref ref; 1400 srcEntry->GetRef(&ref); 1401 1402 char destName[B_FILE_NAME_LENGTH]; 1403 strcpy(destName, ref.name); 1404 1405 loopControl->UpdateStatus(ref.name, ref, 1024, true); 1406 1407 if (makeOriginalName) { 1408 BString suffix(" "); 1409 suffix << B_TRANSLATE_COMMENT("copy", "filename copy"), 1410 FSMakeOriginalName(destName, destDir, suffix.String()); 1411 undo.UpdateEntry(srcEntry, destName); 1412 } 1413 1414 if (destDir->FindEntry(destName, &existingEntry) == B_OK) { 1415 // some entry with a conflicting name is already present in destDir 1416 // decide what to do about it 1417 bool isDirectory = existingEntry.IsDirectory(); 1418 1419 switch (loopControl->OverwriteOnConflict(srcEntry, destName, destDir, 1420 true, isDirectory)) { 1421 case TrackerCopyLoopControl::kSkip: 1422 // we are about to ignore this entire directory 1423 return; 1424 1425 1426 case TrackerCopyLoopControl::kReplace: 1427 if (!isDirectory) { 1428 // conflicting with a file or symbolic link, remove entry 1429 ThrowOnError(existingEntry.Remove()); 1430 break; 1431 } 1432 // fall through if directory, do not replace. 1433 case TrackerCopyLoopControl::kMerge: 1434 ASSERT(isDirectory); 1435 // do not create a new directory, use the current one 1436 newDir.SetTo(&existingEntry); 1437 createDirectory = false; 1438 break; 1439 } 1440 } 1441 1442 // loop through everything in src folder and copy it to new folder 1443 BDirectory srcDir(srcEntry); 1444 srcDir.Rewind(); 1445 1446 // create a new folder inside of destination folder 1447 if (createDirectory) { 1448 err = destDir->CreateDirectory(destName, &newDir); 1449 #ifdef _SILENTLY_CORRECT_FILE_NAMES 1450 if (err == B_BAD_VALUE) { 1451 // check if it's an invalid name on a FAT32 file system 1452 if (CreateFileSystemCompatibleName(destDir, destName)) 1453 err = destDir->CreateDirectory(destName, &newDir); 1454 } 1455 #endif 1456 if (err != B_OK) { 1457 if (!loopControl->FileError(B_TRANSLATE_NOCOLLECT( 1458 kFolderErrorString), destName, err, true)) { 1459 throw err; 1460 } 1461 1462 // will allow rest of copy to continue 1463 return; 1464 } 1465 } 1466 1467 char *buffer; 1468 if (createDirectory && err == B_OK 1469 && (buffer = (char*)malloc(32768)) != 0) { 1470 CopyAttributes(loopControl, &srcDir, &newDir, buffer, 32768); 1471 // don't copy original pose location if new location passed 1472 free(buffer); 1473 } 1474 1475 StatStruct statbuf; 1476 srcDir.GetStat(&statbuf); 1477 dev_t sourceDeviceID = statbuf.st_dev; 1478 1479 // copy or write new pose location 1480 node_ref destNodeRef; 1481 destDir->GetNodeRef(&destNodeRef); 1482 SetUpPoseLocation(ref.directory, destNodeRef.node, &srcDir, 1483 &newDir, loc); 1484 1485 while (srcDir.GetNextEntry(&entry) == B_OK) { 1486 1487 if (loopControl->CheckUserCanceled()) 1488 throw (status_t)kUserCanceled; 1489 1490 entry.GetStat(&statbuf); 1491 1492 if (S_ISDIR(statbuf.st_mode)) { 1493 1494 // entry is a mount point, do not copy it 1495 if (statbuf.st_dev != sourceDeviceID) { 1496 PRINT(("Avoiding mount point %" B_PRIdDEV ", %" B_PRIdDEV "\n", 1497 statbuf.st_dev, sourceDeviceID)); 1498 continue; 1499 } 1500 1501 CopyFolder(&entry, &newDir, loopControl, 0, false, undo, 1502 removeSource); 1503 if (removeSource) 1504 FSDeleteFolder(&entry, loopControl, true, true, false); 1505 } else { 1506 CopyFile(&entry, &statbuf, &newDir, loopControl, 0, false, undo); 1507 if (removeSource) 1508 entry.Remove(); 1509 } 1510 } 1511 if (removeSource) 1512 srcEntry->Remove(); 1513 else 1514 srcEntry->Unset(); 1515 } 1516 1517 1518 status_t 1519 RecursiveMove(BEntry *entry, BDirectory *destDir, 1520 CopyLoopControl* loopControl) 1521 { 1522 char name[B_FILE_NAME_LENGTH]; 1523 if (entry->GetName(name) == B_OK) { 1524 if (destDir->Contains(name)) { 1525 BPath path (destDir, name); 1526 BDirectory subDir (path.Path()); 1527 entry_ref ref; 1528 entry->GetRef(&ref); 1529 BDirectory source(&ref); 1530 if (source.InitCheck() == B_OK) { 1531 source.Rewind(); 1532 BEntry current; 1533 while (source.GetNextEntry(¤t) == B_OK) { 1534 if (current.IsDirectory()) { 1535 RecursiveMove(¤t, &subDir, loopControl); 1536 current.Remove(); 1537 } else { 1538 current.GetName(name); 1539 if (loopControl->OverwriteOnConflict(¤t, name, 1540 &subDir, true, false) 1541 != TrackerCopyLoopControl::kSkip) { 1542 MoveError::FailOnError(current.MoveTo(&subDir, 1543 NULL, true)); 1544 } 1545 } 1546 } 1547 } 1548 entry->Remove(); 1549 } else { 1550 MoveError::FailOnError(entry->MoveTo(destDir)); 1551 } 1552 } 1553 1554 return B_OK; 1555 } 1556 1557 status_t 1558 MoveItem(BEntry *entry, BDirectory *destDir, BPoint *loc, uint32 moveMode, 1559 const char *newName, Undo &undo, CopyLoopControl* loopControl) 1560 { 1561 entry_ref ref; 1562 try { 1563 node_ref destNode; 1564 StatStruct statbuf; 1565 MoveError::FailOnError(entry->GetStat(&statbuf)); 1566 MoveError::FailOnError(entry->GetRef(&ref)); 1567 MoveError::FailOnError(destDir->GetNodeRef(&destNode)); 1568 1569 if (moveMode == kCreateLink || moveMode == kCreateRelativeLink) { 1570 PoseInfo poseInfo; 1571 char name[B_FILE_NAME_LENGTH]; 1572 strcpy(name, ref.name); 1573 1574 BSymLink link; 1575 BString suffix(" "); 1576 suffix << B_TRANSLATE_COMMENT("link", "filename link"), 1577 FSMakeOriginalName(name, destDir, suffix.String()); 1578 undo.UpdateEntry(entry, name); 1579 1580 BPath path; 1581 entry->GetPath(&path); 1582 if (loc && loc != (BPoint *)-1) { 1583 poseInfo.fInvisible = false; 1584 poseInfo.fInitedDirectory = destNode.node; 1585 poseInfo.fLocation = *loc; 1586 } 1587 1588 status_t err = B_ERROR; 1589 1590 if (moveMode == kCreateRelativeLink) { 1591 if (statbuf.st_dev == destNode.device) { 1592 // relative link only works on the same device 1593 char oldwd[B_PATH_NAME_LENGTH]; 1594 getcwd(oldwd, B_PATH_NAME_LENGTH); 1595 1596 BEntry destEntry; 1597 destDir -> GetEntry(&destEntry); 1598 BPath destPath; 1599 destEntry.GetPath(&destPath); 1600 1601 chdir(destPath.Path()); 1602 // change working dir to target dir 1603 1604 BString destString(destPath.Path()); 1605 destString.Append("/"); 1606 1607 BString srcString(path.Path()); 1608 srcString.RemoveLast(path.Leaf()); 1609 1610 // find index while paths are the same 1611 1612 const char *src = srcString.String(); 1613 const char *dest = destString.String(); 1614 const char *lastFolderSrc = src; 1615 const char *lastFolderDest = dest; 1616 1617 while (*src && *dest && *src == *dest) { 1618 ++src; 1619 if (*dest++ == '/') { 1620 lastFolderSrc = src; 1621 lastFolderDest = dest; 1622 } 1623 } 1624 src = lastFolderSrc; 1625 dest = lastFolderDest; 1626 1627 BString source; 1628 if (*dest == '\0' && *src != '\0') { 1629 // source is deeper in the same tree than the target 1630 source.Append(src); 1631 } else if (*dest != '\0') { 1632 // target is deeper in the same tree than the source 1633 while (*dest) { 1634 if (*dest == '/') 1635 source.Prepend("../"); 1636 ++dest; 1637 } 1638 source.Append(src); 1639 } 1640 1641 // else source and target are in the same dir 1642 1643 source.Append(path.Leaf()); 1644 err = destDir->CreateSymLink(name, source.String(), &link); 1645 1646 chdir(oldwd); 1647 // change working dir back to original 1648 } else 1649 moveMode = kCreateLink; 1650 // fall back to absolute link mode 1651 } 1652 1653 if (moveMode == kCreateLink) 1654 err = destDir->CreateSymLink(name, path.Path(), &link); 1655 1656 if (err == B_UNSUPPORTED) { 1657 throw FailWithAlert(err, 1658 B_TRANSLATE("The target disk does not support " 1659 "creating links."), NULL); 1660 } 1661 1662 FailWithAlert::FailOnError(err, 1663 B_TRANSLATE("Error creating link to \"%name\"."), 1664 ref.name); 1665 1666 if (loc && loc != (BPoint *)-1) { 1667 link.WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &poseInfo, 1668 sizeof(PoseInfo)); 1669 } 1670 1671 BNodeInfo nodeInfo(&link); 1672 nodeInfo.SetType(B_LINK_MIMETYPE); 1673 return B_OK; 1674 } 1675 1676 // if move is on same volume don't copy 1677 if (statbuf.st_dev == destNode.device && moveMode != kCopySelectionTo 1678 && moveMode != kDuplicateSelection) { 1679 1680 // for "Move" the size for status is always 1 - since file 1681 // size is irrelevant when simply moving to a new folder 1682 loopControl->UpdateStatus(ref.name, ref, 1); 1683 if (entry->IsDirectory()) 1684 return RecursiveMove(entry, destDir, loopControl); 1685 MoveError::FailOnError(entry->MoveTo(destDir, newName)); 1686 } else { 1687 bool makeOriginalName = (moveMode == kDuplicateSelection); 1688 if (S_ISDIR(statbuf.st_mode)) { 1689 CopyFolder(entry, destDir, loopControl, loc, makeOriginalName, 1690 undo, moveMode == kMoveSelectionTo); 1691 } else { 1692 CopyFile(entry, &statbuf, destDir, loopControl, loc, 1693 makeOriginalName, undo); 1694 if (moveMode == kMoveSelectionTo) 1695 entry->Remove(); 1696 } 1697 } 1698 } catch (status_t error) { 1699 // no alert, was already taken care of before 1700 return error; 1701 } catch (MoveError error) { 1702 BString errorString(B_TRANSLATE("Error moving \"%name\"")); 1703 errorString.ReplaceFirst("%name", ref.name); 1704 (new BAlert("", errorString.String(), B_TRANSLATE("OK"), 0, 0, 1705 B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(); 1706 return error.fError; 1707 } catch (FailWithAlert error) { 1708 BString buffer(error.fString); 1709 if (error.fName) 1710 buffer.ReplaceFirst("%name", error.fName); 1711 else 1712 buffer << error.fString; 1713 (new BAlert("", buffer.String(), B_TRANSLATE("OK"), 0, 0, 1714 B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(); 1715 1716 return error.fError; 1717 } 1718 1719 return B_OK; 1720 } 1721 1722 1723 void 1724 FSDuplicate(BObjectList<entry_ref> *srcList, BList *pointList) 1725 { 1726 LaunchInNewThread("DupTask", B_NORMAL_PRIORITY, MoveTask, srcList, 1727 (BEntry *)NULL, pointList, kDuplicateSelection); 1728 } 1729 1730 1731 #if 0 1732 status_t 1733 FSCopyFolder(BEntry *srcEntry, BDirectory *destDir, 1734 CopyLoopControl *loopControl, BPoint *loc, bool makeOriginalName) 1735 { 1736 try 1737 CopyFolder(srcEntry, destDir, loopControl, loc, makeOriginalName); 1738 catch (status_t error) { 1739 return error; 1740 1741 return B_OK; 1742 } 1743 #endif 1744 1745 1746 status_t 1747 FSCopyAttributesAndStats(BNode *srcNode, BNode *destNode) 1748 { 1749 char *buffer = new char[1024]; 1750 1751 // copy the attributes 1752 srcNode->RewindAttrs(); 1753 char name[256]; 1754 while (srcNode->GetNextAttrName(name) == B_OK) { 1755 attr_info info; 1756 if (srcNode->GetAttrInfo(name, &info) != B_OK) 1757 continue; 1758 1759 attr_info dest_info; 1760 if (destNode->GetAttrInfo(name, &dest_info) == B_OK) 1761 continue; 1762 1763 ssize_t bytes; 1764 ssize_t numToRead = (ssize_t)info.size; 1765 for (off_t offset = 0; numToRead > 0; offset += bytes) { 1766 size_t chunkSize = (size_t)numToRead; 1767 if (chunkSize > 1024) 1768 chunkSize = 1024; 1769 1770 bytes = srcNode->ReadAttr(name, info.type, offset, buffer, 1771 chunkSize); 1772 1773 if (bytes <= 0) 1774 break; 1775 1776 destNode->WriteAttr(name, info.type, offset, buffer, 1777 (size_t)bytes); 1778 1779 numToRead -= bytes; 1780 } 1781 } 1782 delete[] buffer; 1783 1784 // copy the file stats 1785 struct stat srcStat; 1786 srcNode->GetStat(&srcStat); 1787 destNode->SetPermissions(srcStat.st_mode); 1788 destNode->SetOwner(srcStat.st_uid); 1789 destNode->SetGroup(srcStat.st_gid); 1790 destNode->SetModificationTime(srcStat.st_mtime); 1791 destNode->SetCreationTime(srcStat.st_crtime); 1792 1793 return B_OK; 1794 } 1795 1796 1797 #if 0 1798 status_t 1799 FSCopyFile(BEntry* srcFile, StatStruct *srcStat, BDirectory* destDir, 1800 CopyLoopControl *loopControl, BPoint *loc, bool makeOriginalName) 1801 { 1802 try { 1803 CopyFile(srcFile, srcStat, destDir, loopControl, loc, 1804 makeOriginalName); 1805 } catch (status_t error) { 1806 return error; 1807 } 1808 1809 return B_OK; 1810 } 1811 #endif 1812 1813 1814 static status_t 1815 MoveEntryToTrash(BEntry *entry, BPoint *loc, Undo &undo) 1816 { 1817 BDirectory trash_dir; 1818 entry_ref ref; 1819 status_t result = entry->GetRef(&ref); 1820 if (result != B_OK) 1821 return result; 1822 1823 node_ref nodeRef; 1824 result = entry->GetNodeRef(&nodeRef); 1825 if (result != B_OK) 1826 return result; 1827 1828 StatStruct statbuf; 1829 result = entry->GetStat(&statbuf); 1830 if (entry->GetStat(&statbuf) != B_OK) 1831 return result; 1832 1833 // if it's a directory close the window and any child dir windows 1834 if (S_ISDIR(statbuf.st_mode)) { 1835 BDirectory dir(entry); 1836 1837 // if it's a volume, try to unmount 1838 if (dir.IsRootDirectory()) { 1839 BVolume volume(nodeRef.device); 1840 BVolume boot; 1841 1842 BVolumeRoster().GetBootVolume(&boot); 1843 if (volume == boot) { 1844 char name[B_FILE_NAME_LENGTH]; 1845 volume.GetName(name); 1846 BString buffer(B_TRANSLATE("Cannot unmount the boot volume \"%name\".")); 1847 buffer.ReplaceFirst("%name", name); 1848 BAlert* alert = new BAlert("", buffer.String(), 1849 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, 1850 B_WARNING_ALERT); 1851 alert->SetShortcut(0, B_ESCAPE); 1852 alert->Go(); 1853 } else { 1854 BMessage message(kUnmountVolume); 1855 message.AddInt32("device_id", volume.Device()); 1856 be_app->PostMessage(&message); 1857 } 1858 return B_OK; 1859 } 1860 1861 // get trash directory on same volume as item being moved 1862 result = FSGetTrashDir(&trash_dir, nodeRef.device); 1863 if (result != B_OK) 1864 return result; 1865 1866 // check hierarchy before moving 1867 BEntry trashEntry; 1868 trash_dir.GetEntry(&trashEntry); 1869 1870 if (dir == trash_dir || dir.Contains(&trashEntry)) { 1871 (new BAlert("", 1872 B_TRANSLATE("You cannot put the Trash, home or Desktop " 1873 "directory into the trash."), 1874 B_TRANSLATE("OK"), 1875 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(); 1876 1877 // return no error so we don't get two dialogs 1878 return B_OK; 1879 } 1880 1881 BMessage message(kCloseWindowAndChildren); 1882 1883 node_ref parentNode; 1884 parentNode.device = statbuf.st_dev; 1885 parentNode.node = statbuf.st_ino; 1886 message.AddData("node_ref", B_RAW_TYPE, &parentNode, sizeof(node_ref)); 1887 be_app->PostMessage(&message); 1888 } else { 1889 // get trash directory on same volume as item being moved 1890 result = FSGetTrashDir(&trash_dir, nodeRef.device); 1891 if (result != B_OK) 1892 return result; 1893 } 1894 1895 // make sure name doesn't conflict with anything in trash already 1896 char name[B_FILE_NAME_LENGTH]; 1897 strcpy(name, ref.name); 1898 if (trash_dir.Contains(name)) { 1899 BString suffix(" "); 1900 suffix << B_TRANSLATE_COMMENT("copy", "filename copy"), 1901 FSMakeOriginalName(name, &trash_dir, suffix.String()); 1902 undo.UpdateEntry(entry, name); 1903 } 1904 1905 BNode *src_node = 0; 1906 if (loc && loc != (BPoint *)-1 1907 && (src_node = GetWritableNode(entry, &statbuf)) != 0) { 1908 trash_dir.GetStat(&statbuf); 1909 PoseInfo poseInfo; 1910 poseInfo.fInvisible = false; 1911 poseInfo.fInitedDirectory = statbuf.st_ino; 1912 poseInfo.fLocation = *loc; 1913 src_node->WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &poseInfo, 1914 sizeof(poseInfo)); 1915 delete src_node; 1916 } 1917 1918 BNode node(entry); 1919 BPath path; 1920 // Get path of entry before it's moved to the trash 1921 // and write it to the file as an attribute 1922 if (node.InitCheck() == B_OK && entry->GetPath(&path) == B_OK) { 1923 BString originalPath(path.Path()); 1924 node.WriteAttrString(kAttrOriginalPath, &originalPath); 1925 } 1926 1927 TrackerCopyLoopControl loopControl; 1928 MoveItem(entry, &trash_dir, loc, kMoveSelectionTo, name, undo, 1929 &loopControl); 1930 return B_OK; 1931 } 1932 1933 1934 ConflictCheckResult 1935 PreFlightNameCheck(BObjectList<entry_ref> *srcList, const BDirectory *destDir, 1936 int32 *collisionCount, uint32 moveMode) 1937 { 1938 1939 // count the number of name collisions in dest folder 1940 *collisionCount = 0; 1941 1942 int32 count = srcList->CountItems(); 1943 for (int32 i = 0; i < count; i++) { 1944 entry_ref *srcRef = srcList->ItemAt(i); 1945 BEntry entry(srcRef); 1946 BDirectory parent; 1947 entry.GetParent(&parent); 1948 1949 if (parent != *destDir) { 1950 if (destDir->Contains(srcRef->name)) 1951 (*collisionCount)++; 1952 } 1953 } 1954 1955 // prompt user only if there is more than one collision, otherwise the 1956 // single collision case will be handled as a "Prompt" case by CheckName 1957 if (*collisionCount > 1) { 1958 const char* verb = (moveMode == kMoveSelectionTo) 1959 ? B_TRANSLATE("moving") : B_TRANSLATE("copying"); 1960 BString replaceMsg(B_TRANSLATE_NOCOLLECT(kReplaceManyStr)); 1961 replaceMsg.ReplaceAll("%verb", verb); 1962 1963 BAlert* alert = new BAlert("", replaceMsg.String(), 1964 B_TRANSLATE("Cancel"), B_TRANSLATE("Prompt"), 1965 B_TRANSLATE("Replace all")); 1966 alert->SetShortcut(0, B_ESCAPE); 1967 switch (alert->Go()) { 1968 case 0: 1969 return kCanceled; 1970 1971 case 1: 1972 // user selected "Prompt" 1973 return kPrompt; 1974 1975 case 2: 1976 // user selected "Replace All" 1977 return kReplaceAll; 1978 } 1979 } 1980 1981 return kNoConflicts; 1982 } 1983 1984 1985 void 1986 FileStatToString(StatStruct *stat, char *buffer, int32 length) 1987 { 1988 tm timeData; 1989 localtime_r(&stat->st_mtime, &timeData); 1990 1991 1992 uint32 pos = sprintf(buffer, 1993 B_TRANSLATE("\n\t(%Ld bytes, "), stat->st_size); 1994 strftime(buffer + pos, length - pos,"%b %d %Y, %I:%M:%S %p)", &timeData); 1995 } 1996 1997 1998 status_t 1999 CheckName(uint32 moveMode, const BEntry *sourceEntry, 2000 const BDirectory *destDir, bool multipleCollisions, 2001 ConflictCheckResult &replaceAll) 2002 { 2003 if (moveMode == kDuplicateSelection) 2004 // when duplicating, we will never have a conflict 2005 return B_OK; 2006 2007 // see if item already exists in destination dir 2008 status_t err = B_OK; 2009 char name[B_FILE_NAME_LENGTH]; 2010 sourceEntry->GetName(name); 2011 bool sourceIsDirectory = sourceEntry->IsDirectory(); 2012 2013 BDirectory srcDirectory; 2014 if (sourceIsDirectory) { 2015 srcDirectory.SetTo(sourceEntry); 2016 BEntry destEntry; 2017 destDir->GetEntry(&destEntry); 2018 2019 if (moveMode != kCreateLink 2020 && moveMode != kCreateRelativeLink 2021 && (srcDirectory == *destDir 2022 || srcDirectory.Contains(&destEntry))) { 2023 (new BAlert("", 2024 B_TRANSLATE("You can't move a folder into itself " 2025 "or any of its own sub-folders."), B_TRANSLATE("OK"), 2026 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(); 2027 return B_ERROR; 2028 } 2029 } 2030 2031 if (FSIsTrashDir(sourceEntry) && moveMode != kCreateLink 2032 && moveMode != kCreateRelativeLink) { 2033 (new BAlert("", 2034 B_TRANSLATE("You can't move or copy the trash."), 2035 B_TRANSLATE("OK"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(); 2036 return B_ERROR; 2037 } 2038 2039 BEntry entry; 2040 if (destDir->FindEntry(name, &entry) != B_OK) 2041 // no conflict, return 2042 return B_OK; 2043 2044 if (moveMode == kCreateLink || moveMode == kCreateRelativeLink) { 2045 // if we are creating link in the same directory, the conflict will 2046 // be handled later by giving the link a unique name 2047 sourceEntry->GetParent(&srcDirectory); 2048 2049 if (srcDirectory == *destDir) 2050 return B_OK; 2051 } 2052 2053 bool destIsDir = entry.IsDirectory(); 2054 // be sure not to replace the parent directory of the item being moved 2055 if (destIsDir) { 2056 BDirectory test_dir(&entry); 2057 if (test_dir.Contains(sourceEntry)) { 2058 (new BAlert("", 2059 B_TRANSLATE("You can't replace a folder " 2060 "with one of its sub-folders."), 2061 B_TRANSLATE("OK"), 2062 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(); 2063 return B_ERROR; 2064 } 2065 } 2066 2067 // Ensure user isn't trying to replace a file with folder or vice versa. 2068 if (moveMode != kCreateLink 2069 && moveMode != kCreateRelativeLink 2070 && destIsDir != sourceIsDirectory) { 2071 (new BAlert("", sourceIsDirectory 2072 ? B_TRANSLATE("You cannot replace a file with a folder or a " 2073 "symbolic link.") 2074 : B_TRANSLATE("You cannot replace a folder or a symbolic link " 2075 "with a file."), B_TRANSLATE("OK"), 0, 0, B_WIDTH_AS_USUAL, 2076 B_WARNING_ALERT))->Go(); 2077 return B_ERROR; 2078 } 2079 2080 if (replaceAll != kReplaceAll) { 2081 // prompt user to determine whether to replace or not 2082 2083 BString replaceMsg; 2084 2085 if (moveMode == kCreateLink || moveMode == kCreateRelativeLink) { 2086 replaceMsg.SetTo(B_TRANSLATE_NOCOLLECT(kSymLinkReplaceStr)); 2087 replaceMsg.ReplaceFirst("%name", name); 2088 } else if (sourceEntry->IsDirectory()) { 2089 replaceMsg.SetTo(B_TRANSLATE_NOCOLLECT(kDirectoryReplaceStr)); 2090 replaceMsg.ReplaceFirst("%name", name); 2091 replaceMsg.ReplaceFirst("%verb", 2092 moveMode == kMoveSelectionTo 2093 ? B_TRANSLATE("moving") 2094 : B_TRANSLATE("copying")); 2095 } else { 2096 char sourceBuffer[96], destBuffer[96]; 2097 StatStruct statBuffer; 2098 2099 if (!sourceEntry->IsDirectory() && sourceEntry->GetStat( 2100 &statBuffer) == B_OK) { 2101 FileStatToString(&statBuffer, sourceBuffer, 96); 2102 } else 2103 sourceBuffer[0] = '\0'; 2104 2105 if (!entry.IsDirectory() && entry.GetStat(&statBuffer) == B_OK) 2106 FileStatToString(&statBuffer, destBuffer, 96); 2107 else 2108 destBuffer[0] = '\0'; 2109 2110 replaceMsg.SetTo(B_TRANSLATE_NOCOLLECT(kReplaceStr)); 2111 replaceMsg.ReplaceAll("%name", name); 2112 replaceMsg.ReplaceFirst("%dest", destBuffer); 2113 replaceMsg.ReplaceFirst("%src", sourceBuffer); 2114 replaceMsg.ReplaceFirst("%movemode", 2115 moveMode == kMoveSelectionTo 2116 ? B_TRANSLATE("moving") 2117 : B_TRANSLATE("copying")); 2118 } 2119 2120 // special case single collision (don't need Replace All shortcut) 2121 BAlert *alert; 2122 if (multipleCollisions || sourceIsDirectory) { 2123 alert = new BAlert("", replaceMsg.String(), 2124 B_TRANSLATE("Skip"), B_TRANSLATE("Replace all")); 2125 } else { 2126 alert = new BAlert("", replaceMsg.String(), 2127 B_TRANSLATE("Cancel"), B_TRANSLATE("Replace")); 2128 alert->SetShortcut(0, B_ESCAPE); 2129 } 2130 switch (alert->Go()) { 2131 case 0: // user selected "Cancel" or "Skip" 2132 replaceAll = kCanceled; 2133 return B_ERROR; 2134 2135 case 1: // user selected "Replace" or "Replace All" 2136 replaceAll = kReplaceAll; 2137 // doesn't matter which since a single 2138 // collision "Replace" is equivalent to a 2139 // "Replace All" 2140 break; 2141 } 2142 } 2143 2144 // delete destination item 2145 if (!destIsDir) 2146 err = entry.Remove(); 2147 else 2148 return B_OK; 2149 2150 if (err != B_OK) { 2151 BString error(B_TRANSLATE("There was a problem trying to replace \"%name\". The item might be open or busy.")); 2152 error.ReplaceFirst("%name", name);; 2153 BAlert* alert = new BAlert("", error.String(), 2154 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 2155 alert->SetShortcut(0, B_ESCAPE); 2156 alert->Go(); 2157 } 2158 2159 return err; 2160 } 2161 2162 2163 status_t 2164 FSDeleteFolder(BEntry *dir_entry, CopyLoopControl *loopControl, 2165 bool update_status, bool delete_top_dir, bool upateFileNameInStatus) 2166 { 2167 entry_ref ref; 2168 BEntry entry; 2169 BDirectory dir; 2170 status_t err; 2171 2172 dir.SetTo(dir_entry); 2173 dir.Rewind(); 2174 // loop through everything in folder and delete it, skipping trouble files 2175 for (;;) { 2176 if (dir.GetNextEntry(&entry) != B_OK) 2177 break; 2178 2179 entry.GetRef(&ref); 2180 2181 if (loopControl->CheckUserCanceled()) 2182 return kTrashCanceled; 2183 2184 if (entry.IsDirectory()) 2185 err = FSDeleteFolder(&entry, loopControl, update_status, true, 2186 upateFileNameInStatus); 2187 else { 2188 err = entry.Remove(); 2189 if (update_status) { 2190 loopControl->UpdateStatus(upateFileNameInStatus ? ref.name 2191 : "", ref, 1, true); 2192 } 2193 } 2194 2195 if (err == kTrashCanceled) 2196 return kTrashCanceled; 2197 else if (err == B_OK) 2198 dir.Rewind(); 2199 else { 2200 loopControl->FileError(B_TRANSLATE_NOCOLLECT( 2201 kFileDeleteErrorString), ref.name, err, false); 2202 } 2203 } 2204 2205 if (loopControl->CheckUserCanceled()) 2206 return kTrashCanceled; 2207 2208 dir_entry->GetRef(&ref); 2209 2210 if (update_status && delete_top_dir) 2211 loopControl->UpdateStatus(NULL, ref, 1); 2212 2213 if (delete_top_dir) 2214 return dir_entry->Remove(); 2215 else 2216 return B_OK; 2217 } 2218 2219 2220 void 2221 FSMakeOriginalName(BString &string, const BDirectory *destDir, 2222 const char *suffix) 2223 { 2224 if (!destDir->Contains(string.String())) 2225 return; 2226 2227 FSMakeOriginalName(string.LockBuffer(B_FILE_NAME_LENGTH), 2228 const_cast<BDirectory *>(destDir), suffix ? suffix : " copy"); 2229 string.UnlockBuffer(); 2230 } 2231 2232 2233 void 2234 FSMakeOriginalName(char *name, BDirectory *destDir, const char *suffix) 2235 { 2236 char root[B_FILE_NAME_LENGTH]; 2237 char copybase[B_FILE_NAME_LENGTH]; 2238 char temp_name[B_FILE_NAME_LENGTH + 10]; 2239 int32 fnum; 2240 2241 // is this name already original? 2242 if (!destDir->Contains(name)) 2243 return; 2244 2245 // Determine if we're copying a 'copy'. This algorithm isn't perfect. 2246 // If you're copying a file whose REAL name ends with 'copy' then 2247 // this method will return "<filename> 1", not "<filename> copy" 2248 2249 // However, it will correctly handle file that contain 'copy' 2250 // elsewhere in their name. 2251 2252 bool copycopy = false; // are we copying a copy? 2253 int32 len = (int32)strlen(name); 2254 char *p = name + len - 1; // get pointer to end os name 2255 2256 // eat up optional numbers (if were copying "<filename> copy 34") 2257 while ((p > name) && isdigit(*p)) 2258 p--; 2259 2260 // eat up optional spaces 2261 while ((p > name) && isspace(*p)) 2262 p--; 2263 2264 // now look for the phrase " copy" 2265 if (p > name) { 2266 // p points to the last char of the word. For example, 'y' in 'copy' 2267 2268 if ((p - 4 > name) && (strncmp(p - 4, suffix, 5) == 0)) { 2269 // we found 'copy' in the right place. 2270 // so truncate after 'copy' 2271 *(p + 1) = '\0'; 2272 copycopy = true; 2273 2274 // save the 'root' name of the file, for possible later use. 2275 // that is copy everything but trailing " copy". Need to 2276 // NULL terminate after copy 2277 strncpy(root, name, (uint32)((p - name) - 4)); 2278 root[(p - name) - 4] = '\0'; 2279 } 2280 } 2281 2282 if (!copycopy) { 2283 /* 2284 The name can't be longer than B_FILE_NAME_LENGTH. 2285 The algoritm adds " copy XX" to the name. That's 8 characters. 2286 B_FILE_NAME_LENGTH already accounts for NULL termination so we 2287 don't need to save an extra char at the end. 2288 */ 2289 if (strlen(name) > B_FILE_NAME_LENGTH - 8) { 2290 // name is too long - truncate it! 2291 name[B_FILE_NAME_LENGTH - 8] = '\0'; 2292 } 2293 2294 strcpy(root, name); // save root name 2295 strcat(name, suffix); 2296 } 2297 2298 strcpy(copybase, name); 2299 2300 // if name already exists then add a number 2301 fnum = 1; 2302 strcpy(temp_name, name); 2303 while (destDir->Contains(temp_name)) { 2304 sprintf(temp_name, "%s %ld", copybase, ++fnum); 2305 2306 if (strlen(temp_name) > (B_FILE_NAME_LENGTH - 1)) { 2307 /* 2308 The name has grown too long. Maybe we just went from 2309 "<filename> copy 9" to "<filename> copy 10" and that extra 2310 character was too much. The solution is to further 2311 truncate the 'root' name and continue. 2312 ??? should we reset fnum or not ??? 2313 */ 2314 root[strlen(root) - 1] = '\0'; 2315 sprintf(temp_name, "%s%s %ld", root, suffix, fnum); 2316 } 2317 } 2318 2319 ASSERT((strlen(temp_name) <= (B_FILE_NAME_LENGTH - 1))); 2320 strcpy(name, temp_name); 2321 } 2322 2323 2324 status_t 2325 FSRecursiveCalcSize(BInfoWindow *window, CopyLoopControl* loopControl, 2326 BDirectory *dir, off_t *running_size, int32 *fileCount, int32 *dirCount) 2327 { 2328 dir->Rewind(); 2329 BEntry entry; 2330 while (dir->GetNextEntry(&entry) == B_OK) { 2331 // be sure window hasn't closed 2332 if (window && window->StopCalc()) 2333 return B_OK; 2334 2335 if (loopControl->CheckUserCanceled()) 2336 return kUserCanceled; 2337 2338 StatStruct statbuf; 2339 entry.GetStat(&statbuf); 2340 2341 if (S_ISDIR(statbuf.st_mode)) { 2342 BDirectory subdir(&entry); 2343 (*dirCount)++; 2344 (*running_size) += 1024; 2345 status_t result = FSRecursiveCalcSize(window, loopControl, &subdir, 2346 running_size, fileCount, dirCount); 2347 if (result != B_OK) 2348 return result; 2349 } else { 2350 (*fileCount)++; 2351 (*running_size) += statbuf.st_size + 1024; 2352 // Add to compensate for attributes. 2353 } 2354 } 2355 return B_OK; 2356 } 2357 2358 2359 status_t 2360 CalcItemsAndSize(CopyLoopControl* loopControl, BObjectList<entry_ref> *refList, 2361 size_t blockSize, int32 *totalCount, off_t *totalSize) 2362 { 2363 int32 fileCount = 0; 2364 int32 dirCount = 0; 2365 2366 // check block size for sanity 2367 if (blockSize < 0) { 2368 // This would point at an error to retrieve the block size from 2369 // the target volume. The code below cannot be used, it is only 2370 // meant to get the block size when item operations happen on 2371 // the source volume. 2372 blockSize = 2048; 2373 } else if (blockSize < 1024) { 2374 blockSize = 1024; 2375 if (entry_ref* ref = refList->ItemAt(0)) { 2376 // TODO: This assumes all entries in the list share the same 2377 // volume... 2378 BVolume volume(ref->device); 2379 if (volume.InitCheck() == B_OK) 2380 blockSize = volume.BlockSize(); 2381 } 2382 } 2383 // File systems like ReiserFS may advertize a large block size, but 2384 // stuff is still packed into blocks, so clamp maximum block size. 2385 if (blockSize > 8192) 2386 blockSize = 8192; 2387 2388 int32 num_items = refList->CountItems(); 2389 for (int32 i = 0; i < num_items; i++) { 2390 entry_ref *ref = refList->ItemAt(i); 2391 BEntry entry(ref); 2392 StatStruct statbuf; 2393 entry.GetStat(&statbuf); 2394 2395 if (loopControl->CheckUserCanceled()) 2396 return kUserCanceled; 2397 2398 if (S_ISDIR(statbuf.st_mode)) { 2399 BDirectory dir(&entry); 2400 dirCount++; 2401 (*totalSize) += blockSize; 2402 status_t result = FSRecursiveCalcSize(NULL, loopControl, &dir, 2403 totalSize, &fileCount, &dirCount); 2404 if (result != B_OK) 2405 return result; 2406 } else { 2407 fileCount++; 2408 (*totalSize) += statbuf.st_size + blockSize; 2409 } 2410 } 2411 2412 *totalCount += (fileCount + dirCount); 2413 return B_OK; 2414 } 2415 2416 2417 status_t 2418 FSGetTrashDir(BDirectory *trashDir, dev_t dev) 2419 { 2420 BVolume volume(dev); 2421 status_t result = volume.InitCheck(); 2422 if (result != B_OK) 2423 return result; 2424 2425 BPath path; 2426 result = find_directory(B_TRASH_DIRECTORY, &path, false, &volume); 2427 if (result != B_OK) 2428 return result; 2429 2430 result = trashDir->SetTo(path.Path()); 2431 if (result == B_OK) { 2432 // Directory already exists, we're done 2433 return B_OK; 2434 } 2435 2436 // The trash directory does not exist yet - change that! 2437 2438 result = create_directory(path.Path(), 0755); 2439 if (result != B_OK) 2440 return result; 2441 2442 result = trashDir->SetTo(path.Path()); 2443 if (result != B_OK) 2444 return result; 2445 2446 // make trash invisible 2447 StatStruct sbuf; 2448 trashDir->GetStat(&sbuf); 2449 2450 PoseInfo poseInfo; 2451 poseInfo.fInvisible = true; 2452 poseInfo.fInitedDirectory = sbuf.st_ino; 2453 trashDir->WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &poseInfo, 2454 sizeof(PoseInfo)); 2455 2456 // set trash icon 2457 size_t size; 2458 const void* data 2459 = GetTrackerResources()->LoadResource('ICON', R_TrashIcon, &size); 2460 if (data != NULL) 2461 trashDir->WriteAttr(kAttrLargeIcon, 'ICON', 0, data, size); 2462 2463 data = GetTrackerResources()->LoadResource('MICN', R_TrashIcon, &size); 2464 if (data != NULL) 2465 trashDir->WriteAttr(kAttrMiniIcon, 'MICN', 0, data, size); 2466 2467 data = GetTrackerResources()->LoadResource(B_VECTOR_ICON_TYPE, R_TrashIcon, 2468 &size); 2469 if (data != NULL) 2470 trashDir->WriteAttr(kAttrIcon, B_VECTOR_ICON_TYPE, 0, data, size); 2471 2472 return B_OK; 2473 } 2474 2475 2476 #if __GNUC__ && __GNUC__ < 3 2477 // obsolete version of FSGetDeskDir retained for bin compat with 2478 // BeIDE and a few other apps that apparently use it 2479 status_t 2480 FSGetDeskDir(BDirectory *deskDir, dev_t) 2481 { 2482 // since we no longer keep a desktop directory on any volume other 2483 // than /boot, redirect to FSGetDeskDir ignoring the volume argument 2484 return FSGetDeskDir(deskDir); 2485 } 2486 #endif 2487 2488 2489 status_t 2490 FSGetDeskDir(BDirectory *deskDir) 2491 { 2492 BPath path; 2493 status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true); 2494 if (result != B_OK) 2495 return result; 2496 2497 result = deskDir->SetTo(path.Path()); 2498 if (result != B_OK) 2499 return result; 2500 2501 size_t size; 2502 const void* data = GetTrackerResources()-> 2503 LoadResource('ICON', R_DeskIcon, &size); 2504 if (data != NULL) 2505 deskDir->WriteAttr(kAttrLargeIcon, 'ICON', 0, data, size); 2506 2507 data = GetTrackerResources()-> 2508 LoadResource('MICN', R_DeskIcon, &size); 2509 if (data != NULL) 2510 deskDir->WriteAttr(kAttrMiniIcon, 'MICN', 0, data, size); 2511 2512 #ifdef __HAIKU__ 2513 data = GetTrackerResources()-> 2514 LoadResource(B_VECTOR_ICON_TYPE, R_DeskIcon, &size); 2515 if (data != NULL) 2516 deskDir->WriteAttr(kAttrIcon, B_VECTOR_ICON_TYPE, 0, data, size); 2517 #endif 2518 2519 return B_OK; 2520 } 2521 2522 2523 status_t 2524 FSGetBootDeskDir(BDirectory *deskDir) 2525 { 2526 BVolume bootVol; 2527 BVolumeRoster().GetBootVolume(&bootVol); 2528 BPath path; 2529 2530 status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true, 2531 &bootVol); 2532 if (result != B_OK) 2533 return result; 2534 2535 return deskDir->SetTo(path.Path()); 2536 } 2537 2538 2539 static bool 2540 FSIsDirFlavor(const BEntry *entry, directory_which directoryType) 2541 { 2542 StatStruct dir_stat; 2543 StatStruct entry_stat; 2544 BVolume volume; 2545 BPath path; 2546 2547 if (entry->GetStat(&entry_stat) != B_OK) 2548 return false; 2549 2550 if (volume.SetTo(entry_stat.st_dev) != B_OK) 2551 return false; 2552 2553 if (find_directory(directoryType, &path, false, &volume) != B_OK) 2554 return false; 2555 2556 stat(path.Path(), &dir_stat); 2557 2558 return dir_stat.st_ino == entry_stat.st_ino 2559 && dir_stat.st_dev == entry_stat.st_dev; 2560 } 2561 2562 2563 bool 2564 FSIsPrintersDir(const BEntry *entry) 2565 { 2566 return FSIsDirFlavor(entry, B_USER_PRINTERS_DIRECTORY); 2567 } 2568 2569 2570 bool 2571 FSIsTrashDir(const BEntry *entry) 2572 { 2573 return FSIsDirFlavor(entry, B_TRASH_DIRECTORY); 2574 } 2575 2576 2577 bool 2578 FSIsDeskDir(const BEntry *entry) 2579 { 2580 BPath path; 2581 status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true); 2582 if (result != B_OK) 2583 return false; 2584 2585 BEntry entryToCompare(path.Path()); 2586 return entryToCompare == *entry; 2587 } 2588 2589 2590 bool 2591 FSIsHomeDir(const BEntry *entry) 2592 { 2593 return FSIsDirFlavor(entry, B_USER_DIRECTORY); 2594 } 2595 2596 2597 bool 2598 DirectoryMatchesOrContains(const BEntry *entry, directory_which which) 2599 { 2600 BPath path; 2601 if (find_directory(which, &path, false, NULL) != B_OK) 2602 return false; 2603 2604 BEntry dirEntry(path.Path()); 2605 if (dirEntry.InitCheck() != B_OK) 2606 return false; 2607 2608 if (dirEntry == *entry) 2609 // root level match 2610 return true; 2611 2612 BDirectory dir(&dirEntry); 2613 return dir.Contains(entry); 2614 } 2615 2616 2617 bool 2618 DirectoryMatchesOrContains(const BEntry *entry, const char *additionalPath, 2619 directory_which which) 2620 { 2621 BPath path; 2622 if (find_directory(which, &path, false, NULL) != B_OK) 2623 return false; 2624 2625 path.Append(additionalPath); 2626 BEntry dirEntry(path.Path()); 2627 if (dirEntry.InitCheck() != B_OK) 2628 return false; 2629 2630 if (dirEntry == *entry) 2631 // root level match 2632 return true; 2633 2634 BDirectory dir(&dirEntry); 2635 return dir.Contains(entry); 2636 } 2637 2638 2639 bool 2640 DirectoryMatches(const BEntry *entry, directory_which which) 2641 { 2642 BPath path; 2643 if (find_directory(which, &path, false, NULL) != B_OK) 2644 return false; 2645 2646 BEntry dirEntry(path.Path()); 2647 if (dirEntry.InitCheck() != B_OK) 2648 return false; 2649 2650 return dirEntry == *entry; 2651 } 2652 2653 2654 bool 2655 DirectoryMatches(const BEntry *entry, const char *additionalPath, 2656 directory_which which) 2657 { 2658 BPath path; 2659 if (find_directory(which, &path, false, NULL) != B_OK) 2660 return false; 2661 2662 path.Append(additionalPath); 2663 BEntry dirEntry(path.Path()); 2664 if (dirEntry.InitCheck() != B_OK) 2665 return false; 2666 2667 return dirEntry == *entry; 2668 } 2669 2670 2671 extern status_t 2672 FSFindTrackerSettingsDir(BPath *path, bool autoCreate) 2673 { 2674 status_t result = find_directory (B_USER_SETTINGS_DIRECTORY, path, 2675 autoCreate); 2676 if (result != B_OK) 2677 return result; 2678 2679 path->Append("Tracker"); 2680 2681 return mkdir(path->Path(), 0777) ? B_OK : errno; 2682 } 2683 2684 2685 bool 2686 FSInTrashDir(const entry_ref *ref) 2687 { 2688 BEntry entry(ref); 2689 if (entry.InitCheck() != B_OK) 2690 return false; 2691 2692 BDirectory trashDir; 2693 if (FSGetTrashDir(&trashDir, ref->device) != B_OK) 2694 return false; 2695 2696 return trashDir.Contains(&entry); 2697 } 2698 2699 2700 void 2701 FSEmptyTrash() 2702 { 2703 if (find_thread("_tracker_empty_trash_") != B_OK) { 2704 resume_thread(spawn_thread(empty_trash, "_tracker_empty_trash_", 2705 B_NORMAL_PRIORITY, NULL)); 2706 } 2707 } 2708 2709 2710 status_t 2711 empty_trash(void *) 2712 { 2713 // empty trash on all mounted volumes 2714 status_t err = B_OK; 2715 2716 TrackerCopyLoopControl loopControl(kTrashState); 2717 2718 // calculate the sum total of all items on all volumes in trash 2719 BObjectList<entry_ref> srcList; 2720 int32 totalCount = 0; 2721 off_t totalSize = 0; 2722 2723 BVolumeRoster volumeRoster; 2724 BVolume volume; 2725 while (volumeRoster.GetNextVolume(&volume) == B_OK) { 2726 if (volume.IsReadOnly() || !volume.IsPersistent()) 2727 continue; 2728 2729 BDirectory trashDirectory; 2730 if (FSGetTrashDir(&trashDirectory, volume.Device()) != B_OK) 2731 continue; 2732 2733 BEntry entry; 2734 trashDirectory.GetEntry(&entry); 2735 2736 entry_ref ref; 2737 entry.GetRef(&ref); 2738 srcList.AddItem(&ref); 2739 err = CalcItemsAndSize(&loopControl, &srcList, volume.BlockSize(), 2740 &totalCount, &totalSize); 2741 if (err != B_OK) 2742 break; 2743 2744 srcList.MakeEmpty(); 2745 2746 // don't count trash directory itself 2747 totalCount--; 2748 } 2749 2750 if (err == B_OK) { 2751 loopControl.Init(totalCount, totalCount); 2752 2753 volumeRoster.Rewind(); 2754 while (volumeRoster.GetNextVolume(&volume) == B_OK) { 2755 if (volume.IsReadOnly() || !volume.IsPersistent()) 2756 continue; 2757 2758 BDirectory trashDirectory; 2759 if (FSGetTrashDir(&trashDirectory, volume.Device()) != B_OK) 2760 continue; 2761 2762 BEntry entry; 2763 trashDirectory.GetEntry(&entry); 2764 err = FSDeleteFolder(&entry, &loopControl, true, false); 2765 } 2766 } 2767 2768 if (err != B_OK && err != kTrashCanceled && err != kUserCanceled) { 2769 (new BAlert("", B_TRANSLATE("Error emptying Trash!"), 2770 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, 2771 B_WARNING_ALERT))->Go(); 2772 } 2773 2774 return B_OK; 2775 } 2776 2777 2778 status_t 2779 _DeleteTask(BObjectList<entry_ref> *list, bool confirm) 2780 { 2781 if (confirm) { 2782 bool dontMoveToTrash = TrackerSettings().DontMoveFilesToTrash(); 2783 2784 if (!dontMoveToTrash) { 2785 BAlert* alert = new BAlert("", 2786 B_TRANSLATE_NOCOLLECT(kDeleteConfirmationStr), 2787 B_TRANSLATE("Cancel"), B_TRANSLATE("Move to Trash"), 2788 B_TRANSLATE("Delete"), B_WIDTH_AS_USUAL, B_OFFSET_SPACING, 2789 B_WARNING_ALERT); 2790 2791 alert->SetShortcut(0, B_ESCAPE); 2792 alert->SetShortcut(1, 'm'); 2793 alert->SetShortcut(2, 'd'); 2794 2795 switch (alert->Go()) { 2796 case 0: 2797 delete list; 2798 return B_OK; 2799 case 1: 2800 FSMoveToTrash(list, NULL, false); 2801 return B_OK; 2802 } 2803 } else { 2804 BAlert* alert = new BAlert("", 2805 B_TRANSLATE_NOCOLLECT(kDeleteConfirmationStr), 2806 B_TRANSLATE("Cancel"), B_TRANSLATE("Delete"), NULL, 2807 B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_WARNING_ALERT); 2808 2809 alert->SetShortcut(0, B_ESCAPE); 2810 alert->SetShortcut(1, 'd'); 2811 2812 if (!alert->Go()) { 2813 delete list; 2814 return B_OK; 2815 } 2816 } 2817 } 2818 2819 TrackerCopyLoopControl loopControl(kDeleteState); 2820 2821 // calculate the sum total of all items on all volumes in trash 2822 int32 totalItems = 0; 2823 int64 totalSize = 0; 2824 2825 status_t err = CalcItemsAndSize(&loopControl, list, 0, &totalItems, 2826 &totalSize); 2827 if (err == B_OK) { 2828 loopControl.Init(totalItems, totalItems); 2829 2830 int32 count = list->CountItems(); 2831 for (int32 index = 0; index < count; index++) { 2832 entry_ref ref(*list->ItemAt(index)); 2833 BEntry entry(&ref); 2834 loopControl.UpdateStatus(ref.name, ref, 1, true); 2835 if (entry.IsDirectory()) 2836 err = FSDeleteFolder(&entry, &loopControl, true, true, true); 2837 else 2838 err = entry.Remove(); 2839 } 2840 2841 if (err != kTrashCanceled && err != kUserCanceled && err != B_OK) 2842 (new BAlert("", B_TRANSLATE("Error deleting items"), 2843 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, 2844 B_WARNING_ALERT))->Go(); 2845 } 2846 2847 delete list; 2848 2849 return B_OK; 2850 } 2851 2852 status_t 2853 FSRecursiveCreateFolder(BPath path) 2854 { 2855 BEntry entry(path.Path()); 2856 if (entry.InitCheck() != B_OK) { 2857 BPath parentPath; 2858 status_t err = path.GetParent(&parentPath); 2859 if (err != B_OK) 2860 return err; 2861 2862 err = FSRecursiveCreateFolder(parentPath); 2863 if (err != B_OK) 2864 return err; 2865 } 2866 2867 entry.SetTo(path.Path()); 2868 if (entry.Exists()) 2869 return B_FILE_EXISTS; 2870 else { 2871 char name[B_FILE_NAME_LENGTH]; 2872 BDirectory parent; 2873 2874 entry.GetParent(&parent); 2875 entry.GetName(name); 2876 parent.CreateDirectory(name, NULL); 2877 } 2878 2879 return B_OK; 2880 } 2881 2882 status_t 2883 _RestoreTask(BObjectList<entry_ref> *list) 2884 { 2885 TrackerCopyLoopControl loopControl(kRestoreFromTrashState); 2886 2887 // calculate the sum total of all items that will be restored 2888 int32 totalItems = 0; 2889 int64 totalSize = 0; 2890 2891 status_t err = CalcItemsAndSize(&loopControl, list, 0, &totalItems, 2892 &totalSize); 2893 if (err == B_OK) { 2894 loopControl.Init(totalItems, totalItems); 2895 2896 int32 count = list->CountItems(); 2897 for (int32 index = 0; index < count; index++) { 2898 entry_ref ref(*list->ItemAt(index)); 2899 BEntry entry(&ref); 2900 BPath originalPath; 2901 2902 loopControl.UpdateStatus(ref.name, ref, 1, true); 2903 2904 if (FSGetOriginalPath(&entry, &originalPath) != B_OK) 2905 continue; 2906 2907 BEntry originalEntry(originalPath.Path()); 2908 BPath parentPath; 2909 err = originalPath.GetParent(&parentPath); 2910 if (err != B_OK) 2911 continue; 2912 BEntry parentEntry(parentPath.Path()); 2913 2914 if (parentEntry.InitCheck() != B_OK || !parentEntry.Exists()) { 2915 if (FSRecursiveCreateFolder(parentPath) == B_OK) { 2916 originalEntry.SetTo(originalPath.Path()); 2917 if (entry.InitCheck() != B_OK) 2918 continue; 2919 } 2920 } 2921 2922 if (!originalEntry.Exists()) { 2923 BDirectory dir(parentPath.Path()); 2924 if (dir.InitCheck() == B_OK) { 2925 char leafName[B_FILE_NAME_LENGTH]; 2926 originalEntry.GetName(leafName); 2927 if (entry.MoveTo(&dir, leafName) == B_OK) { 2928 BNode node(&entry); 2929 if (node.InitCheck() == B_OK) 2930 node.RemoveAttr(kAttrOriginalPath); 2931 } 2932 } 2933 } 2934 2935 err = loopControl.CheckUserCanceled(); 2936 if (err != B_OK) 2937 break; 2938 } 2939 } 2940 2941 delete list; 2942 2943 return err; 2944 } 2945 2946 void 2947 FSCreateTrashDirs() 2948 { 2949 BVolume volume; 2950 BVolumeRoster roster; 2951 2952 roster.Rewind(); 2953 while (roster.GetNextVolume(&volume) == B_OK) { 2954 if (volume.IsReadOnly() || !volume.IsPersistent()) 2955 continue; 2956 2957 BDirectory trashDir; 2958 FSGetTrashDir(&trashDir, volume.Device()); 2959 } 2960 } 2961 2962 2963 status_t 2964 FSCreateNewFolder(const entry_ref *ref) 2965 { 2966 node_ref node; 2967 node.device = ref->device; 2968 node.node = ref->directory; 2969 2970 BDirectory dir(&node); 2971 status_t result = dir.InitCheck(); 2972 if (result != B_OK) 2973 return result; 2974 2975 // ToDo: is that really necessary here? 2976 BString name(ref->name); 2977 FSMakeOriginalName(name, &dir, "-"); 2978 2979 BDirectory newDir; 2980 result = dir.CreateDirectory(name.String(), &newDir); 2981 if (result != B_OK) 2982 return result; 2983 2984 BNodeInfo nodeInfo(&newDir); 2985 nodeInfo.SetType(B_DIR_MIMETYPE); 2986 2987 return result; 2988 } 2989 2990 2991 status_t 2992 FSCreateNewFolderIn(const node_ref *dirNode, entry_ref *newRef, 2993 node_ref *newNode) 2994 { 2995 BDirectory dir(dirNode); 2996 status_t result = dir.InitCheck(); 2997 if (result == B_OK) { 2998 char name[B_FILE_NAME_LENGTH]; 2999 strcpy(name, B_TRANSLATE("New folder")); 3000 3001 int32 fnum = 1; 3002 while (dir.Contains(name)) { 3003 // if base name already exists then add a number 3004 // ToDo: 3005 // move this logic ot FSMakeOriginalName 3006 if (++fnum > 9) { 3007 snprintf(name, sizeof(name), B_TRANSLATE("New folder%ld"), 3008 fnum); 3009 } else { 3010 snprintf(name, sizeof(name), B_TRANSLATE("New folder %ld"), 3011 fnum); 3012 } 3013 } 3014 3015 BDirectory newDir; 3016 result = dir.CreateDirectory(name, &newDir); 3017 if (result == B_OK) { 3018 BEntry entry; 3019 newDir.GetEntry(&entry); 3020 entry.GetRef(newRef); 3021 entry.GetNodeRef(newNode); 3022 3023 BNodeInfo nodeInfo(&newDir); 3024 nodeInfo.SetType(B_DIR_MIMETYPE); 3025 3026 // add undo item 3027 NewFolderUndo undo(*newRef); 3028 return B_OK; 3029 } 3030 } 3031 3032 BAlert* alert = new BAlert("", 3033 B_TRANSLATE("Sorry, could not create a new folder."), 3034 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 3035 alert->SetShortcut(0, B_ESCAPE); 3036 alert->Go(); 3037 return result; 3038 } 3039 3040 3041 ReadAttrResult 3042 ReadAttr(const BNode *node, const char *hostAttrName, 3043 const char *foreignAttrName, type_code type, off_t offset, void *buffer, 3044 size_t length, void (*swapFunc)(void *), bool isForeign) 3045 { 3046 if (!isForeign && node->ReadAttr(hostAttrName, type, offset, buffer, 3047 length) == (ssize_t)length) { 3048 return kReadAttrNativeOK; 3049 } 3050 3051 // PRINT(("trying %s\n", foreignAttrName)); 3052 // try the other endianness 3053 if (node->ReadAttr(foreignAttrName, type, offset, buffer, length) 3054 != (ssize_t)length) { 3055 return kReadAttrFailed; 3056 } 3057 3058 // PRINT(("got %s\n", foreignAttrName)); 3059 if (!swapFunc) 3060 return kReadAttrForeignOK; 3061 3062 (swapFunc)(buffer); 3063 // run the endian swapper 3064 3065 return kReadAttrForeignOK; 3066 } 3067 3068 3069 ReadAttrResult 3070 GetAttrInfo(const BNode *node, const char *hostAttrName, 3071 const char *foreignAttrName, type_code *type, size_t *size) 3072 { 3073 attr_info info; 3074 3075 if (node->GetAttrInfo(hostAttrName, &info) == B_OK) { 3076 if (type) 3077 *type = info.type; 3078 if (size) 3079 *size = (size_t)info.size; 3080 3081 return kReadAttrNativeOK; 3082 } 3083 3084 if (node->GetAttrInfo(foreignAttrName, &info) == B_OK) { 3085 if (type) 3086 *type = info.type; 3087 if (size) 3088 *size = (size_t)info.size; 3089 3090 return kReadAttrForeignOK; 3091 } 3092 return kReadAttrFailed; 3093 } 3094 3095 // launching code 3096 3097 static status_t 3098 TrackerOpenWith(const BMessage *refs) 3099 { 3100 BMessage clone(*refs); 3101 ASSERT(dynamic_cast<TTracker *>(be_app)); 3102 ASSERT(clone.what); 3103 clone.AddInt32("launchUsingSelector", 0); 3104 // runs the Open With window 3105 be_app->PostMessage(&clone); 3106 3107 return B_OK; 3108 } 3109 3110 3111 static void 3112 AsynchLaunchBinder(void (*func)(const entry_ref *, const BMessage *, bool on), 3113 const entry_ref *appRef, const BMessage *refs, bool openWithOK) 3114 { 3115 BMessage *task = new BMessage; 3116 task->AddPointer("function", (void *)func); 3117 task->AddMessage("refs", refs); 3118 task->AddBool("openWithOK", openWithOK); 3119 if (appRef != NULL) 3120 task->AddRef("appRef", appRef); 3121 3122 extern BLooper *gLaunchLooper; 3123 gLaunchLooper->PostMessage(task); 3124 } 3125 3126 static bool 3127 SniffIfGeneric(const entry_ref *ref) 3128 { 3129 BNode node(ref); 3130 char type[B_MIME_TYPE_LENGTH]; 3131 BNodeInfo info(&node); 3132 if (info.GetType(type) == B_OK 3133 && strcasecmp(type, B_FILE_MIME_TYPE) != 0) { 3134 // already has a type and it's not octet stream 3135 return false; 3136 } 3137 3138 BPath path(ref); 3139 if (path.Path()) { 3140 // force a mimeset 3141 node.RemoveAttr(kAttrMIMEType); 3142 update_mime_info(path.Path(), 0, 1, 1); 3143 } 3144 3145 return true; 3146 } 3147 3148 static void 3149 SniffIfGeneric(const BMessage *refs) 3150 { 3151 entry_ref ref; 3152 for (int32 index = 0; ; index++) { 3153 if (refs->FindRef("refs", index, &ref) != B_OK) 3154 break; 3155 SniffIfGeneric(&ref); 3156 } 3157 } 3158 3159 static void 3160 _TrackerLaunchAppWithDocuments(const entry_ref *appRef, const BMessage *refs, 3161 bool openWithOK) 3162 { 3163 team_id team; 3164 3165 status_t error = B_ERROR; 3166 BString alertString; 3167 3168 for (int32 mimesetIt = 0; ; mimesetIt++) { 3169 error = be_roster->Launch(appRef, refs, &team); 3170 if (error == B_ALREADY_RUNNING) 3171 // app already running, not really an error 3172 error = B_OK; 3173 3174 if (error == B_OK) 3175 break; 3176 3177 if (mimesetIt > 0) 3178 break; 3179 3180 // failed to open, try mimesetting the refs and launching again 3181 SniffIfGeneric(refs); 3182 } 3183 3184 if (error == B_OK) { 3185 // close possible parent window, if specified 3186 const node_ref *nodeToClose = 0; 3187 int32 numBytes; 3188 refs->FindData("nodeRefsToClose", B_RAW_TYPE, 3189 (const void **)&nodeToClose, &numBytes); 3190 if (nodeToClose) 3191 dynamic_cast<TTracker *>(be_app)->CloseParent(*nodeToClose); 3192 } else { 3193 alertString.SetTo(B_TRANSLATE("Could not open \"%name\" (%error). ")); 3194 alertString.ReplaceFirst("%name", appRef->name); 3195 alertString.ReplaceFirst("%error", strerror(error)); 3196 if (refs && openWithOK && error != B_SHUTTING_DOWN) { 3197 alertString << B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3198 BAlert* alert = new BAlert("", alertString.String(), 3199 B_TRANSLATE("Cancel"), B_TRANSLATE("Find"), 0, B_WIDTH_AS_USUAL, 3200 B_WARNING_ALERT); 3201 alert->SetShortcut(0, B_ESCAPE); 3202 if (alert->Go() == 1) 3203 error = TrackerOpenWith(refs); 3204 } else { 3205 BAlert* alert = new BAlert("", alertString.String(), 3206 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 3207 alert->SetShortcut(0, B_ESCAPE); 3208 alert->Go(); 3209 } 3210 } 3211 } 3212 3213 extern "C" char** environ; 3214 3215 extern "C" status_t _kern_load_image(const char * const *flatArgs, 3216 size_t flatArgsSize, int32 argCount, int32 envCount, int32 priority, 3217 uint32 flags, port_id errorPort, uint32 errorToken); 3218 extern "C" status_t __flatten_process_args(const char * const *args, 3219 int32 argCount, const char * const *env, int32 envCount, char ***_flatArgs, 3220 size_t *_flatSize); 3221 3222 3223 static status_t 3224 LoaderErrorDetails(const entry_ref *app, BString &details) 3225 { 3226 BPath path; 3227 BEntry appEntry(app, true); 3228 3229 status_t result = appEntry.GetPath(&path); 3230 if (result != B_OK) 3231 return result; 3232 3233 char *argv[2] = { const_cast<char *>(path.Path()), 0}; 3234 3235 port_id errorPort = create_port(1, "Tracker loader error"); 3236 3237 // count environment variables 3238 uint32 envCount = 0; 3239 while (environ[envCount] != NULL) 3240 envCount++; 3241 3242 char** flatArgs = NULL; 3243 size_t flatArgsSize; 3244 result = __flatten_process_args((const char **)argv, 1, 3245 environ, envCount, &flatArgs, &flatArgsSize); 3246 if (result != B_OK) 3247 return result; 3248 3249 result = _kern_load_image(flatArgs, flatArgsSize, 1, envCount, 3250 B_NORMAL_PRIORITY, B_WAIT_TILL_LOADED, errorPort, 0); 3251 if (result == B_OK) { 3252 // we weren't supposed to be able to start the application... 3253 return B_ERROR; 3254 } 3255 3256 // read error message from port and construct details string 3257 3258 ssize_t bufferSize; 3259 3260 do { 3261 bufferSize = port_buffer_size_etc(errorPort, B_RELATIVE_TIMEOUT, 0); 3262 } while (bufferSize == B_INTERRUPTED); 3263 3264 if (bufferSize <= B_OK) { 3265 delete_port(errorPort); 3266 return bufferSize; 3267 } 3268 3269 uint8 *buffer = (uint8 *)malloc(bufferSize); 3270 if (buffer == NULL) { 3271 delete_port(errorPort); 3272 return B_NO_MEMORY; 3273 } 3274 3275 bufferSize = read_port_etc(errorPort, NULL, buffer, bufferSize, 3276 B_RELATIVE_TIMEOUT, 0); 3277 delete_port(errorPort); 3278 3279 if (bufferSize < B_OK) { 3280 free(buffer); 3281 return bufferSize; 3282 } 3283 3284 BMessage message; 3285 result = message.Unflatten((const char *)buffer); 3286 free(buffer); 3287 3288 if (result != B_OK) 3289 return result; 3290 3291 int32 errorCode = B_ERROR; 3292 result = message.FindInt32("error", &errorCode); 3293 if (result != B_OK) 3294 return result; 3295 3296 const char *detailName = NULL; 3297 switch (errorCode) { 3298 case B_MISSING_LIBRARY: 3299 detailName = "missing library"; 3300 break; 3301 3302 case B_MISSING_SYMBOL: 3303 detailName = "missing symbol"; 3304 break; 3305 } 3306 3307 if (detailName == NULL) 3308 return B_ERROR; 3309 3310 const char *detail; 3311 for (int32 i = 0; message.FindString(detailName, i, &detail) == B_OK; 3312 i++) { 3313 if (i > 0) 3314 details += ", "; 3315 details += detail; 3316 } 3317 3318 return B_OK; 3319 } 3320 3321 3322 static void 3323 _TrackerLaunchDocuments(const entry_ref */*doNotUse*/, const BMessage *refs, 3324 bool openWithOK) 3325 { 3326 BMessage copyOfRefs(*refs); 3327 3328 entry_ref documentRef; 3329 if (copyOfRefs.FindRef("refs", &documentRef) != B_OK) 3330 // nothing to launch, we are done 3331 return; 3332 3333 status_t error = B_ERROR; 3334 entry_ref app; 3335 BMessage *refsToPass = NULL; 3336 BString alertString; 3337 const char *alternative = 0; 3338 3339 for (int32 mimesetIt = 0; ; mimesetIt++) { 3340 alertString = ""; 3341 error = be_roster->FindApp(&documentRef, &app); 3342 3343 if (error != B_OK && mimesetIt == 0) { 3344 SniffIfGeneric(©OfRefs); 3345 continue; 3346 } 3347 3348 if (error != B_OK) { 3349 alertString.SetTo(B_TRANSLATE("Could not find an application to " 3350 "open \"%name\" (%error). ")); 3351 alertString.ReplaceFirst("%name", documentRef.name); 3352 alertString.ReplaceFirst("%error", strerror(error)); 3353 if (openWithOK) 3354 alternative = B_TRANSLATE_NOCOLLECT(kFindApplicationStr); 3355 3356 break; 3357 } else { 3358 BEntry appEntry(&app, true); 3359 for (int32 index = 0;;) { 3360 // remove the app itself from the refs received so we don't try 3361 // to open ourselves 3362 entry_ref ref; 3363 if (copyOfRefs.FindRef("refs", index, &ref) != B_OK) 3364 break; 3365 3366 // deal with symlinks properly 3367 BEntry documentEntry(&ref, true); 3368 if (appEntry == documentEntry) { 3369 PRINT(("stripping %s, app %s \n", ref.name, app.name)); 3370 copyOfRefs.RemoveData("refs", index); 3371 } else { 3372 PRINT(("leaving %s, app %s \n", ref.name, app.name)); 3373 index++; 3374 } 3375 } 3376 3377 refsToPass = CountRefs(©OfRefs) > 0 ? ©OfRefs: 0; 3378 team_id team; 3379 error = be_roster->Launch(&app, refsToPass, &team); 3380 if (error == B_ALREADY_RUNNING) 3381 // app already running, not really an error 3382 error = B_OK; 3383 if (error == B_OK || mimesetIt != 0) 3384 break; 3385 3386 SniffIfGeneric(©OfRefs); 3387 } 3388 } 3389 3390 if (error != B_OK && alertString.Length() == 0) { 3391 BString loaderErrorString; 3392 bool openedDocuments = true; 3393 3394 if (!refsToPass) { 3395 // we just double clicked the app itself, do not offer to 3396 // find a handling app 3397 openWithOK = false; 3398 openedDocuments = false; 3399 } 3400 3401 if (error == B_LAUNCH_FAILED_EXECUTABLE && !refsToPass) { 3402 alertString.SetTo(B_TRANSLATE("Could not open \"%name\". " 3403 "The file is mistakenly marked as executable. ")); 3404 alertString.ReplaceFirst("%name", app.name); 3405 3406 if (!openWithOK) { 3407 // offer the possibility to change the permissions 3408 3409 alertString << B_TRANSLATE("\nShould this be fixed?"); 3410 BAlert* alert = new BAlert("", alertString.String(), 3411 B_TRANSLATE("Cancel"), B_TRANSLATE("Proceed"), 0, 3412 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 3413 alert->SetShortcut(0, B_ESCAPE); 3414 if (alert->Go() == 1) { 3415 BEntry entry(&documentRef); 3416 mode_t permissions; 3417 3418 error = entry.GetPermissions(&permissions); 3419 if (error == B_OK) { 3420 error = entry.SetPermissions(permissions 3421 & ~(S_IXUSR | S_IXGRP | S_IXOTH)); 3422 } 3423 if (error == B_OK) { 3424 // we updated the permissions, so let's try again 3425 _TrackerLaunchDocuments(NULL, refs, false); 3426 return; 3427 } else { 3428 alertString.SetTo(B_TRANSLATE("Could not update " 3429 "permissions of file \"%name\". %error")); 3430 alertString.ReplaceFirst("%name", app.name); 3431 alertString.ReplaceFirst("%error", strerror(error)); 3432 } 3433 } else 3434 return; 3435 } 3436 3437 alternative = B_TRANSLATE_NOCOLLECT(kFindApplicationStr); 3438 } else if (error == B_LAUNCH_FAILED_APP_IN_TRASH) { 3439 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" " 3440 "because application \"%app\" is in the Trash. ")); 3441 alertString.ReplaceFirst("%document", documentRef.name); 3442 alertString.ReplaceFirst("%app", app.name); 3443 alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3444 } else if (error == B_LAUNCH_FAILED_APP_NOT_FOUND) { 3445 alertString.SetTo( 3446 B_TRANSLATE("Could not open \"%name\" (%error). ")); 3447 alertString.ReplaceFirst("%name", documentRef.name); 3448 alertString.ReplaceFirst("%error", strerror(error)); 3449 alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3450 } else if (error == B_MISSING_SYMBOL 3451 && LoaderErrorDetails(&app, loaderErrorString) == B_OK) { 3452 if (openedDocuments) { 3453 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" " 3454 "with application \"%app\" (Missing symbol: %symbol). \n")); 3455 alertString.ReplaceFirst("%document", documentRef.name); 3456 alertString.ReplaceFirst("%app", app.name); 3457 alertString.ReplaceFirst("%symbol", loaderErrorString.String()); 3458 } else { 3459 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" " 3460 "(Missing symbol: %symbol). \n")); 3461 alertString.ReplaceFirst("%document", documentRef.name); 3462 alertString.ReplaceFirst("%symbol", loaderErrorString.String()); 3463 } 3464 alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3465 } else if (error == B_MISSING_LIBRARY 3466 && LoaderErrorDetails(&app, loaderErrorString) == B_OK) { 3467 if (openedDocuments) { 3468 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" " 3469 "with application \"%app\" (Missing libraries: %library). " 3470 "\n")); 3471 alertString.ReplaceFirst("%document", documentRef.name); 3472 alertString.ReplaceFirst("%app", app.name); 3473 alertString.ReplaceFirst("%library", loaderErrorString.String()); 3474 } else { 3475 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" " 3476 "(Missing libraries: %library). \n")); 3477 alertString.ReplaceFirst("%document", documentRef.name); 3478 alertString.ReplaceFirst("%library", loaderErrorString.String()); 3479 } 3480 alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3481 } else { 3482 alertString.SetTo(B_TRANSLATE("Could not open \"%document\" with " 3483 "application \"%app\" (%error). ")); 3484 alertString.ReplaceFirst("%document", documentRef.name); 3485 alertString.ReplaceFirst("%app", app.name); 3486 alertString.ReplaceFirst("%error", strerror(error)); 3487 alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr); 3488 } 3489 } 3490 3491 if (error != B_OK) { 3492 if (openWithOK) { 3493 ASSERT(alternative); 3494 alertString << alternative; 3495 BAlert* alert = new BAlert("", alertString.String(), 3496 B_TRANSLATE("Cancel"), B_TRANSLATE("Find"), 0, B_WIDTH_AS_USUAL, 3497 B_WARNING_ALERT); 3498 alert->SetShortcut(0, B_ESCAPE); 3499 if (alert->Go() == 1) 3500 error = TrackerOpenWith(refs); 3501 } else { 3502 BAlert* alert = new BAlert("", alertString.String(), 3503 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 3504 alert->SetShortcut(0, B_ESCAPE); 3505 alert->Go(); 3506 } 3507 } 3508 } 3509 3510 // the following three calls don't return any reasonable error codes, 3511 // should fix that, making them void 3512 3513 status_t 3514 TrackerLaunch(const entry_ref *appRef, const BMessage *refs, bool async, 3515 bool openWithOK) 3516 { 3517 if (!async) 3518 _TrackerLaunchAppWithDocuments(appRef, refs, openWithOK); 3519 else { 3520 AsynchLaunchBinder(&_TrackerLaunchAppWithDocuments, appRef, refs, 3521 openWithOK); 3522 } 3523 3524 return B_OK; 3525 } 3526 3527 status_t 3528 TrackerLaunch(const entry_ref *appRef, bool async) 3529 { 3530 if (!async) 3531 _TrackerLaunchAppWithDocuments(appRef, 0, false); 3532 else 3533 AsynchLaunchBinder(&_TrackerLaunchAppWithDocuments, appRef, 0, false); 3534 3535 return B_OK; 3536 } 3537 3538 status_t 3539 TrackerLaunch(const BMessage *refs, bool async, bool openWithOK) 3540 { 3541 if (!async) 3542 _TrackerLaunchDocuments(0, refs, openWithOK); 3543 else 3544 AsynchLaunchBinder(&_TrackerLaunchDocuments, 0, refs, openWithOK); 3545 3546 return B_OK; 3547 } 3548 3549 status_t 3550 LaunchBrokenLink(const char *signature, const BMessage *refs) 3551 { 3552 // This call is to support a hacky workaround for double-clicking 3553 // broken refs for cifs 3554 be_roster->Launch(signature, const_cast<BMessage *>(refs)); 3555 return B_OK; 3556 } 3557 3558 // external launch calls; need to be robust, work if Tracker is not running 3559 3560 #if !B_BEOS_VERSION_DANO 3561 _IMPEXP_TRACKER 3562 #endif 3563 status_t 3564 FSLaunchItem(const entry_ref *application, const BMessage *refsReceived, 3565 bool async, bool openWithOK) 3566 { 3567 return TrackerLaunch(application, refsReceived, async, openWithOK); 3568 } 3569 3570 3571 #if !B_BEOS_VERSION_DANO 3572 _IMPEXP_TRACKER 3573 #endif 3574 status_t 3575 FSOpenWith(BMessage *listOfRefs) 3576 { 3577 status_t result = B_ERROR; 3578 listOfRefs->what = B_REFS_RECEIVED; 3579 3580 if (dynamic_cast<TTracker *>(be_app)) 3581 result = TrackerOpenWith(listOfRefs); 3582 else 3583 ASSERT(!"not yet implemented"); 3584 3585 return result; 3586 } 3587 3588 // legacy calls, need for compatibility 3589 3590 void 3591 FSOpenWithDocuments(const entry_ref *executable, BMessage *documents) 3592 { 3593 TrackerLaunch(executable, documents, true); 3594 delete documents; 3595 } 3596 3597 status_t 3598 FSLaunchUsing(const entry_ref *ref, BMessage *listOfRefs) 3599 { 3600 BMessage temp(B_REFS_RECEIVED); 3601 if (!listOfRefs) { 3602 ASSERT(ref); 3603 temp.AddRef("refs", ref); 3604 listOfRefs = &temp; 3605 } 3606 FSOpenWith(listOfRefs); 3607 return B_OK; 3608 } 3609 3610 status_t 3611 FSLaunchItem(const entry_ref *ref, BMessage* message, int32, bool async) 3612 { 3613 if (message) 3614 message->what = B_REFS_RECEIVED; 3615 3616 status_t result = TrackerLaunch(ref, message, async, true); 3617 delete message; 3618 return result; 3619 } 3620 3621 3622 void 3623 FSLaunchItem(const entry_ref *ref, BMessage *message, int32 workspace) 3624 { 3625 FSLaunchItem(ref, message, workspace, true); 3626 } 3627 3628 // Get the original path of an entry in the trash 3629 status_t 3630 FSGetOriginalPath(BEntry *entry, BPath *result) 3631 { 3632 status_t err; 3633 entry_ref ref; 3634 err = entry->GetRef(&ref); 3635 if (err != B_OK) 3636 return err; 3637 3638 // Only call the routine for entries in the trash 3639 if (!FSInTrashDir(&ref)) 3640 return B_ERROR; 3641 3642 BNode node(entry); 3643 BString originalPath; 3644 if (node.ReadAttrString(kAttrOriginalPath, &originalPath) == B_OK) { 3645 // We're in luck, the entry has the original path in an attribute 3646 err = result->SetTo(originalPath.String()); 3647 return err; 3648 } 3649 3650 // Iterate the parent directories to find one with 3651 // the original path attribute 3652 BEntry parent(*entry); 3653 err = parent.InitCheck(); 3654 if (err != B_OK) 3655 return err; 3656 3657 // walk up the directory structure until we find a node 3658 // with original path attribute 3659 do { 3660 // move to the parent of this node 3661 err = parent.GetParent(&parent); 3662 if (err != B_OK) 3663 return err; 3664 3665 // return if we are at the root of the trash 3666 if (FSIsTrashDir(&parent)) 3667 return B_ENTRY_NOT_FOUND; 3668 3669 // get the parent as a node 3670 err = node.SetTo(&parent); 3671 if (err != B_OK) 3672 return err; 3673 } while (node.ReadAttrString(kAttrOriginalPath, &originalPath) != B_OK); 3674 3675 // Found the attribute, figure out there this file 3676 // used to live, based on the successfully-read attribute 3677 err = result->SetTo(originalPath.String()); 3678 if (err != B_OK) 3679 return err; 3680 3681 BPath path, pathParent; 3682 err = parent.GetPath(&pathParent); 3683 if (err != B_OK) 3684 return err; 3685 err = entry->GetPath(&path); 3686 if (err != B_OK) 3687 return err; 3688 result->Append(path.Path() + strlen(pathParent.Path()) + 1); 3689 // compute the new path by appending the offset of 3690 // the item we are locating, to the original path 3691 // of the parent 3692 return B_OK; 3693 } 3694 3695 directory_which 3696 WellKnowEntryList::Match(const node_ref *node) 3697 { 3698 const WellKnownEntry *result = MatchEntry(node); 3699 if (result) 3700 return result->which; 3701 3702 return (directory_which)-1; 3703 } 3704 3705 const WellKnowEntryList::WellKnownEntry * 3706 WellKnowEntryList::MatchEntry(const node_ref *node) 3707 { 3708 if (!self) 3709 self = new WellKnowEntryList(); 3710 3711 return self->MatchEntryCommon(node); 3712 } 3713 3714 const WellKnowEntryList::WellKnownEntry * 3715 WellKnowEntryList::MatchEntryCommon(const node_ref *node) 3716 { 3717 uint32 count = entries.size(); 3718 for (uint32 index = 0; index < count; index++) 3719 if (*node == entries[index].node) 3720 return &entries[index]; 3721 3722 return NULL; 3723 } 3724 3725 3726 void 3727 WellKnowEntryList::Quit() 3728 { 3729 delete self; 3730 self = NULL; 3731 } 3732 3733 3734 void 3735 WellKnowEntryList::AddOne(directory_which which, const char *name) 3736 { 3737 BPath path; 3738 if (find_directory(which, &path, true) != B_OK) 3739 return; 3740 3741 BEntry entry(path.Path(), true); 3742 node_ref node; 3743 if (entry.GetNodeRef(&node) != B_OK) 3744 return; 3745 3746 entries.push_back(WellKnownEntry(&node, which, name)); 3747 } 3748 3749 3750 void 3751 WellKnowEntryList::AddOne(directory_which which, directory_which base, 3752 const char *extra, const char *name) 3753 { 3754 BPath path; 3755 if (find_directory(base, &path, true) != B_OK) 3756 return; 3757 3758 path.Append(extra); 3759 BEntry entry(path.Path(), true); 3760 node_ref node; 3761 if (entry.GetNodeRef(&node) != B_OK) 3762 return; 3763 3764 entries.push_back(WellKnownEntry(&node, which, name)); 3765 } 3766 3767 3768 void 3769 WellKnowEntryList::AddOne(directory_which which, const char *path, 3770 const char *name) 3771 { 3772 BEntry entry(path, true); 3773 node_ref node; 3774 if (entry.GetNodeRef(&node) != B_OK) 3775 return; 3776 3777 entries.push_back(WellKnownEntry(&node, which, name)); 3778 } 3779 3780 3781 WellKnowEntryList::WellKnowEntryList() 3782 { 3783 #ifdef __HAIKU__ 3784 AddOne(B_SYSTEM_DIRECTORY, "system"); 3785 #else 3786 AddOne(B_BEOS_DIRECTORY, "beos"); 3787 AddOne(B_BEOS_SYSTEM_DIRECTORY, "system"); 3788 #endif 3789 AddOne((directory_which)B_BOOT_DISK, "/boot", "boot"); 3790 AddOne(B_USER_DIRECTORY, "home"); 3791 3792 AddOne(B_BEOS_FONTS_DIRECTORY, "fonts"); 3793 AddOne(B_COMMON_FONTS_DIRECTORY, "fonts"); 3794 AddOne(B_USER_FONTS_DIRECTORY, "fonts"); 3795 3796 AddOne(B_BEOS_APPS_DIRECTORY, "apps"); 3797 AddOne(B_APPS_DIRECTORY, "apps"); 3798 AddOne((directory_which)B_USER_DESKBAR_APPS_DIRECTORY, 3799 B_USER_DESKBAR_DIRECTORY, "Applications", "apps"); 3800 3801 AddOne(B_BEOS_PREFERENCES_DIRECTORY, "preferences"); 3802 AddOne(B_PREFERENCES_DIRECTORY, "preferences"); 3803 AddOne((directory_which)B_USER_DESKBAR_PREFERENCES_DIRECTORY, 3804 B_USER_DESKBAR_DIRECTORY, "Preferences", "preferences"); 3805 3806 AddOne((directory_which)B_USER_MAIL_DIRECTORY, B_USER_DIRECTORY, "mail", 3807 "mail"); 3808 3809 AddOne((directory_which)B_USER_QUERIES_DIRECTORY, B_USER_DIRECTORY, 3810 "queries", "queries"); 3811 3812 3813 3814 AddOne(B_COMMON_DEVELOP_DIRECTORY, "develop"); 3815 AddOne((directory_which)B_USER_DESKBAR_DEVELOP_DIRECTORY, 3816 B_USER_DESKBAR_DIRECTORY, "Development", "develop"); 3817 3818 AddOne(B_USER_CONFIG_DIRECTORY, "config"); 3819 3820 AddOne((directory_which)B_USER_PEOPLE_DIRECTORY, B_USER_DIRECTORY, 3821 "people", "people"); 3822 3823 AddOne((directory_which)B_USER_DOWNLOADS_DIRECTORY, B_USER_DIRECTORY, 3824 "downloads", "downloads"); 3825 } 3826 3827 WellKnowEntryList *WellKnowEntryList::self = NULL; 3828 3829 } // namespace BPrivate 3830