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