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