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