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