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