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