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 BVolume volume(dev); 2360 status_t result = volume.InitCheck(); 2361 if (result != B_OK) 2362 return result; 2363 2364 BPath path; 2365 result = find_directory(B_TRASH_DIRECTORY, &path, false, &volume); 2366 if (result != B_OK) 2367 return result; 2368 2369 result = trashDir->SetTo(path.Path()); 2370 if (result == B_OK) { 2371 // Directory already exists, we're done 2372 return B_OK; 2373 } 2374 2375 // The trash directory does not exist yet - change that! 2376 2377 result = create_directory(path.Path(), 0755); 2378 if (result != B_OK) 2379 return result; 2380 2381 result = trashDir->SetTo(path.Path()); 2382 if (result != B_OK) 2383 return result; 2384 2385 // make trash invisible 2386 StatStruct sbuf; 2387 trashDir->GetStat(&sbuf); 2388 2389 PoseInfo poseInfo; 2390 poseInfo.fInvisible = true; 2391 poseInfo.fInitedDirectory = sbuf.st_ino; 2392 trashDir->WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &poseInfo, 2393 sizeof(PoseInfo)); 2394 2395 // set trash icon 2396 size_t size; 2397 const void* data 2398 = GetTrackerResources()->LoadResource('ICON', R_TrashIcon, &size); 2399 if (data != NULL) 2400 trashDir->WriteAttr(kAttrLargeIcon, 'ICON', 0, data, size); 2401 2402 data = GetTrackerResources()->LoadResource('MICN', R_TrashIcon, &size); 2403 if (data != NULL) 2404 trashDir->WriteAttr(kAttrMiniIcon, 'MICN', 0, data, size); 2405 2406 data = GetTrackerResources()->LoadResource(B_VECTOR_ICON_TYPE, R_TrashIcon, 2407 &size); 2408 if (data != NULL) 2409 trashDir->WriteAttr(kAttrIcon, B_VECTOR_ICON_TYPE, 0, data, size); 2410 2411 return B_OK; 2412 } 2413 2414 2415 #if __GNUC__ && __GNUC__ < 3 2416 // obsolete version of FSGetDeskDir retained for bin compat with 2417 // BeIDE and a few other apps that apparently use it 2418 status_t 2419 FSGetDeskDir(BDirectory *deskDir, dev_t) 2420 { 2421 // since we no longer keep a desktop directory on any volume other 2422 // than /boot, redirect to FSGetDeskDir ignoring the volume argument 2423 return FSGetDeskDir(deskDir); 2424 } 2425 #endif 2426 2427 2428 status_t 2429 FSGetDeskDir(BDirectory *deskDir) 2430 { 2431 BPath path; 2432 status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true); 2433 if (result != B_OK) 2434 return result; 2435 2436 result = deskDir->SetTo(path.Path()); 2437 if (result != B_OK) 2438 return result; 2439 2440 size_t size; 2441 const void* data = GetTrackerResources()-> 2442 LoadResource('ICON', R_DeskIcon, &size); 2443 if (data != NULL) 2444 deskDir->WriteAttr(kAttrLargeIcon, 'ICON', 0, data, size); 2445 2446 data = GetTrackerResources()-> 2447 LoadResource('MICN', R_DeskIcon, &size); 2448 if (data != NULL) 2449 deskDir->WriteAttr(kAttrMiniIcon, 'MICN', 0, data, size); 2450 2451 #ifdef __HAIKU__ 2452 data = GetTrackerResources()-> 2453 LoadResource(B_VECTOR_ICON_TYPE, R_DeskIcon, &size); 2454 if (data != NULL) 2455 deskDir->WriteAttr(kAttrIcon, B_VECTOR_ICON_TYPE, 0, data, size); 2456 #endif 2457 2458 return B_OK; 2459 } 2460 2461 2462 status_t 2463 FSGetBootDeskDir(BDirectory *deskDir) 2464 { 2465 BVolume bootVol; 2466 BVolumeRoster().GetBootVolume(&bootVol); 2467 BPath path; 2468 2469 status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true, 2470 &bootVol); 2471 if (result != B_OK) 2472 return result; 2473 2474 return deskDir->SetTo(path.Path()); 2475 } 2476 2477 2478 static bool 2479 FSIsDirFlavor(const BEntry *entry, directory_which directoryType) 2480 { 2481 StatStruct dir_stat; 2482 StatStruct entry_stat; 2483 BVolume volume; 2484 BPath path; 2485 2486 if (entry->GetStat(&entry_stat) != B_OK) 2487 return false; 2488 2489 if (volume.SetTo(entry_stat.st_dev) != B_OK) 2490 return false; 2491 2492 if (find_directory(directoryType, &path, false, &volume) != B_OK) 2493 return false; 2494 2495 stat(path.Path(), &dir_stat); 2496 2497 return dir_stat.st_ino == entry_stat.st_ino 2498 && dir_stat.st_dev == entry_stat.st_dev; 2499 } 2500 2501 2502 bool 2503 FSIsPrintersDir(const BEntry *entry) 2504 { 2505 return FSIsDirFlavor(entry, B_USER_PRINTERS_DIRECTORY); 2506 } 2507 2508 2509 bool 2510 FSIsTrashDir(const BEntry *entry) 2511 { 2512 return FSIsDirFlavor(entry, B_TRASH_DIRECTORY); 2513 } 2514 2515 2516 bool 2517 FSIsDeskDir(const BEntry *entry) 2518 { 2519 BPath path; 2520 status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true); 2521 if (result != B_OK) 2522 return false; 2523 2524 BEntry entryToCompare(path.Path()); 2525 return entryToCompare == *entry; 2526 } 2527 2528 2529 bool 2530 FSIsHomeDir(const BEntry *entry) 2531 { 2532 return FSIsDirFlavor(entry, B_USER_DIRECTORY); 2533 } 2534 2535 2536 bool 2537 DirectoryMatchesOrContains(const BEntry *entry, directory_which which) 2538 { 2539 BPath path; 2540 if (find_directory(which, &path, false, NULL) != B_OK) 2541 return false; 2542 2543 BEntry dirEntry(path.Path()); 2544 if (dirEntry.InitCheck() != B_OK) 2545 return false; 2546 2547 if (dirEntry == *entry) 2548 // root level match 2549 return true; 2550 2551 BDirectory dir(&dirEntry); 2552 return dir.Contains(entry); 2553 } 2554 2555 2556 bool 2557 DirectoryMatchesOrContains(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 if (dirEntry == *entry) 2570 // root level match 2571 return true; 2572 2573 BDirectory dir(&dirEntry); 2574 return dir.Contains(entry); 2575 } 2576 2577 2578 bool 2579 DirectoryMatches(const BEntry *entry, directory_which which) 2580 { 2581 BPath path; 2582 if (find_directory(which, &path, false, NULL) != B_OK) 2583 return false; 2584 2585 BEntry dirEntry(path.Path()); 2586 if (dirEntry.InitCheck() != B_OK) 2587 return false; 2588 2589 return dirEntry == *entry; 2590 } 2591 2592 2593 bool 2594 DirectoryMatches(const BEntry *entry, const char *additionalPath, 2595 directory_which which) 2596 { 2597 BPath path; 2598 if (find_directory(which, &path, false, NULL) != B_OK) 2599 return false; 2600 2601 path.Append(additionalPath); 2602 BEntry dirEntry(path.Path()); 2603 if (dirEntry.InitCheck() != B_OK) 2604 return false; 2605 2606 return dirEntry == *entry; 2607 } 2608 2609 2610 extern status_t 2611 FSFindTrackerSettingsDir(BPath *path, bool autoCreate) 2612 { 2613 status_t result = find_directory (B_USER_SETTINGS_DIRECTORY, path, 2614 autoCreate); 2615 if (result != B_OK) 2616 return result; 2617 2618 path->Append("Tracker"); 2619 2620 return mkdir(path->Path(), 0777) ? B_OK : errno; 2621 } 2622 2623 2624 bool 2625 FSInTrashDir(const entry_ref *ref) 2626 { 2627 BEntry entry(ref); 2628 if (entry.InitCheck() != B_OK) 2629 return false; 2630 2631 BDirectory trashDir; 2632 if (FSGetTrashDir(&trashDir, ref->device) != B_OK) 2633 return false; 2634 2635 return trashDir.Contains(&entry); 2636 } 2637 2638 2639 void 2640 FSEmptyTrash() 2641 { 2642 if (find_thread("_tracker_empty_trash_") != B_OK) { 2643 resume_thread(spawn_thread(empty_trash, "_tracker_empty_trash_", 2644 B_NORMAL_PRIORITY, NULL)); 2645 } 2646 } 2647 2648 2649 status_t 2650 empty_trash(void *) 2651 { 2652 // empty trash on all mounted volumes 2653 status_t err = B_OK; 2654 2655 TrackerCopyLoopControl loopControl(kTrashState); 2656 2657 // calculate the sum total of all items on all volumes in trash 2658 BObjectList<entry_ref> srcList; 2659 int32 totalCount = 0; 2660 off_t totalSize = 0; 2661 2662 BVolumeRoster volumeRoster; 2663 BVolume volume; 2664 while (volumeRoster.GetNextVolume(&volume) == B_OK) { 2665 if (volume.IsReadOnly() || !volume.IsPersistent()) 2666 continue; 2667 2668 BDirectory trashDirectory; 2669 if (FSGetTrashDir(&trashDirectory, volume.Device()) != B_OK) 2670 continue; 2671 2672 BEntry entry; 2673 trashDirectory.GetEntry(&entry); 2674 2675 entry_ref ref; 2676 entry.GetRef(&ref); 2677 srcList.AddItem(&ref); 2678 err = CalcItemsAndSize(&loopControl, &srcList, volume.BlockSize(), 2679 &totalCount, &totalSize); 2680 if (err != B_OK) 2681 break; 2682 2683 srcList.MakeEmpty(); 2684 2685 // don't count trash directory itself 2686 totalCount--; 2687 } 2688 2689 if (err == B_OK) { 2690 loopControl.Init(totalCount, totalCount); 2691 2692 volumeRoster.Rewind(); 2693 while (volumeRoster.GetNextVolume(&volume) == B_OK) { 2694 if (volume.IsReadOnly() || !volume.IsPersistent()) 2695 continue; 2696 2697 BDirectory trashDirectory; 2698 if (FSGetTrashDir(&trashDirectory, volume.Device()) != B_OK) 2699 continue; 2700 2701 BEntry entry; 2702 trashDirectory.GetEntry(&entry); 2703 err = FSDeleteFolder(&entry, &loopControl, true, false); 2704 } 2705 } 2706 2707 if (err != B_OK && err != kTrashCanceled && err != kUserCanceled) { 2708 (new BAlert("", "Error emptying Trash!", "OK", NULL, NULL, 2709 B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(); 2710 } 2711 2712 return B_OK; 2713 } 2714 2715 2716 status_t 2717 _DeleteTask(BObjectList<entry_ref> *list, bool confirm) 2718 { 2719 if (confirm) { 2720 bool dontMoveToTrash = TrackerSettings().DontMoveFilesToTrash(); 2721 2722 if (!dontMoveToTrash) { 2723 BAlert *alert = new BAlert("", kDeleteConfirmationStr, 2724 "Cancel", "Move to Trash", "Delete", B_WIDTH_AS_USUAL, 2725 B_OFFSET_SPACING, B_WARNING_ALERT); 2726 2727 alert->SetShortcut(0, B_ESCAPE); 2728 alert->SetShortcut(1, 'm'); 2729 alert->SetShortcut(2, 'd'); 2730 2731 switch (alert->Go()) { 2732 case 0: 2733 delete list; 2734 return B_OK; 2735 case 1: 2736 FSMoveToTrash(list, NULL, false); 2737 return B_OK; 2738 } 2739 } else { 2740 BAlert *alert = new BAlert("", kDeleteConfirmationStr, 2741 "Cancel", "Delete", NULL, B_WIDTH_AS_USUAL, B_OFFSET_SPACING, 2742 B_WARNING_ALERT); 2743 2744 alert->SetShortcut(0, B_ESCAPE); 2745 alert->SetShortcut(1, 'd'); 2746 2747 if (!alert->Go()) { 2748 delete list; 2749 return B_OK; 2750 } 2751 } 2752 } 2753 2754 TrackerCopyLoopControl loopControl(kDeleteState); 2755 2756 // calculate the sum total of all items on all volumes in trash 2757 int32 totalItems = 0; 2758 int64 totalSize = 0; 2759 2760 status_t err = CalcItemsAndSize(&loopControl, list, 0, &totalItems, 2761 &totalSize); 2762 if (err == B_OK) { 2763 loopControl.Init(totalItems, totalItems); 2764 2765 int32 count = list->CountItems(); 2766 for (int32 index = 0; index < count; index++) { 2767 entry_ref ref(*list->ItemAt(index)); 2768 BEntry entry(&ref); 2769 loopControl.UpdateStatus(ref.name, ref, 1, true); 2770 if (entry.IsDirectory()) 2771 err = FSDeleteFolder(&entry, &loopControl, true, true, true); 2772 else 2773 err = entry.Remove(); 2774 } 2775 2776 if (err != kTrashCanceled && err != kUserCanceled && err != B_OK) 2777 (new BAlert("", "Error deleting items", "OK", NULL, NULL, 2778 B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(); 2779 } 2780 2781 delete list; 2782 2783 return B_OK; 2784 } 2785 2786 status_t 2787 FSRecursiveCreateFolder(BPath path) 2788 { 2789 BEntry entry(path.Path()); 2790 if (entry.InitCheck() != B_OK) { 2791 BPath parentPath; 2792 status_t err = path.GetParent(&parentPath); 2793 if (err != B_OK) 2794 return err; 2795 2796 err = FSRecursiveCreateFolder(parentPath); 2797 if (err != B_OK) 2798 return err; 2799 } 2800 2801 entry.SetTo(path.Path()); 2802 if (entry.Exists()) 2803 return B_FILE_EXISTS; 2804 else { 2805 char name[B_FILE_NAME_LENGTH]; 2806 BDirectory parent; 2807 2808 entry.GetParent(&parent); 2809 entry.GetName(name); 2810 parent.CreateDirectory(name, NULL); 2811 } 2812 2813 return B_OK; 2814 } 2815 2816 status_t 2817 _RestoreTask(BObjectList<entry_ref> *list) 2818 { 2819 TrackerCopyLoopControl loopControl(kRestoreFromTrashState); 2820 2821 // calculate the sum total of all items that will be restored 2822 int32 totalItems = 0; 2823 int64 totalSize = 0; 2824 2825 status_t err = CalcItemsAndSize(&loopControl, list, 0, &totalItems, 2826 &totalSize); 2827 if (err == B_OK) { 2828 loopControl.Init(totalItems, totalItems); 2829 2830 int32 count = list->CountItems(); 2831 for (int32 index = 0; index < count; index++) { 2832 entry_ref ref(*list->ItemAt(index)); 2833 BEntry entry(&ref); 2834 BPath originalPath; 2835 2836 loopControl.UpdateStatus(ref.name, ref, 1, true); 2837 2838 if (FSGetOriginalPath(&entry, &originalPath) != B_OK) 2839 continue; 2840 2841 BEntry originalEntry(originalPath.Path()); 2842 BPath parentPath; 2843 err = originalPath.GetParent(&parentPath); 2844 if (err != B_OK) 2845 continue; 2846 BEntry parentEntry(parentPath.Path()); 2847 2848 if (parentEntry.InitCheck() != B_OK || !parentEntry.Exists()) { 2849 if (FSRecursiveCreateFolder(parentPath) == B_OK) { 2850 originalEntry.SetTo(originalPath.Path()); 2851 if (entry.InitCheck() != B_OK) 2852 continue; 2853 } 2854 } 2855 2856 if (!originalEntry.Exists()) { 2857 BDirectory dir(parentPath.Path()); 2858 if (dir.InitCheck() == B_OK) { 2859 char leafName[B_FILE_NAME_LENGTH]; 2860 originalEntry.GetName(leafName); 2861 if (entry.MoveTo(&dir, leafName) == B_OK) { 2862 BNode node(&entry); 2863 if (node.InitCheck() == B_OK) 2864 node.RemoveAttr(kAttrOriginalPath); 2865 } 2866 } 2867 } 2868 2869 err = loopControl.CheckUserCanceled(); 2870 if (err != B_OK) 2871 break; 2872 } 2873 } 2874 2875 delete list; 2876 2877 return err; 2878 } 2879 2880 void 2881 FSCreateTrashDirs() 2882 { 2883 BVolume volume; 2884 BVolumeRoster roster; 2885 2886 roster.Rewind(); 2887 while (roster.GetNextVolume(&volume) == B_OK) { 2888 if (volume.IsReadOnly() || !volume.IsPersistent()) 2889 continue; 2890 2891 BDirectory trashDir; 2892 FSGetTrashDir(&trashDir, volume.Device()); 2893 } 2894 } 2895 2896 2897 status_t 2898 FSCreateNewFolder(const entry_ref *ref) 2899 { 2900 node_ref node; 2901 node.device = ref->device; 2902 node.node = ref->directory; 2903 2904 BDirectory dir(&node); 2905 status_t result = dir.InitCheck(); 2906 if (result != B_OK) 2907 return result; 2908 2909 // ToDo: is that really necessary here? 2910 BString name(ref->name); 2911 FSMakeOriginalName(name, &dir, "-"); 2912 2913 BDirectory newDir; 2914 result = dir.CreateDirectory(name.String(), &newDir); 2915 if (result != B_OK) 2916 return result; 2917 2918 BNodeInfo nodeInfo(&newDir); 2919 nodeInfo.SetType(B_DIR_MIMETYPE); 2920 2921 return result; 2922 } 2923 2924 2925 status_t 2926 FSCreateNewFolderIn(const node_ref *dirNode, entry_ref *newRef, 2927 node_ref *newNode) 2928 { 2929 BDirectory dir(dirNode); 2930 status_t result = dir.InitCheck(); 2931 if (result == B_OK) { 2932 char name[B_FILE_NAME_LENGTH]; 2933 strcpy(name, "New folder"); 2934 2935 int32 fnum = 1; 2936 while (dir.Contains(name)) { 2937 // if base name already exists then add a number 2938 // ToDo: 2939 // move this logic ot FSMakeOriginalName 2940 if (++fnum > 9) 2941 sprintf(name, "New folder%ld", fnum); 2942 else 2943 sprintf(name, "New folder %ld", fnum); 2944 } 2945 2946 BDirectory newDir; 2947 result = dir.CreateDirectory(name, &newDir); 2948 if (result == B_OK) { 2949 BEntry entry; 2950 newDir.GetEntry(&entry); 2951 entry.GetRef(newRef); 2952 entry.GetNodeRef(newNode); 2953 2954 BNodeInfo nodeInfo(&newDir); 2955 nodeInfo.SetType(B_DIR_MIMETYPE); 2956 2957 // add undo item 2958 NewFolderUndo undo(*newRef); 2959 return B_OK; 2960 } 2961 } 2962 2963 BAlert *alert = new BAlert("", "Sorry, could not create a new folder.", 2964 "Cancel", 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 2965 alert->SetShortcut(0, B_ESCAPE); 2966 alert->Go(); 2967 return result; 2968 } 2969 2970 2971 ReadAttrResult 2972 ReadAttr(const BNode *node, const char *hostAttrName, 2973 const char *foreignAttrName, type_code type, off_t offset, void *buffer, 2974 size_t length, void (*swapFunc)(void *), bool isForeign) 2975 { 2976 if (!isForeign && node->ReadAttr(hostAttrName, type, offset, buffer, 2977 length) == (ssize_t)length) { 2978 return kReadAttrNativeOK; 2979 } 2980 2981 // PRINT(("trying %s\n", foreignAttrName)); 2982 // try the other endianness 2983 if (node->ReadAttr(foreignAttrName, type, offset, buffer, length) 2984 != (ssize_t)length) { 2985 return kReadAttrFailed; 2986 } 2987 2988 // PRINT(("got %s\n", foreignAttrName)); 2989 if (!swapFunc) 2990 return kReadAttrForeignOK; 2991 2992 (swapFunc)(buffer); 2993 // run the endian swapper 2994 2995 return kReadAttrForeignOK; 2996 } 2997 2998 2999 ReadAttrResult 3000 GetAttrInfo(const BNode *node, const char *hostAttrName, 3001 const char *foreignAttrName, type_code *type, size_t *size) 3002 { 3003 attr_info info; 3004 3005 if (node->GetAttrInfo(hostAttrName, &info) == B_OK) { 3006 if (type) 3007 *type = info.type; 3008 if (size) 3009 *size = (size_t)info.size; 3010 3011 return kReadAttrNativeOK; 3012 } 3013 3014 if (node->GetAttrInfo(foreignAttrName, &info) == B_OK) { 3015 if (type) 3016 *type = info.type; 3017 if (size) 3018 *size = (size_t)info.size; 3019 3020 return kReadAttrForeignOK; 3021 } 3022 return kReadAttrFailed; 3023 } 3024 3025 // launching code 3026 3027 static status_t 3028 TrackerOpenWith(const BMessage *refs) 3029 { 3030 BMessage clone(*refs); 3031 ASSERT(dynamic_cast<TTracker *>(be_app)); 3032 ASSERT(clone.what); 3033 clone.AddInt32("launchUsingSelector", 0); 3034 // runs the Open With window 3035 be_app->PostMessage(&clone); 3036 3037 return B_OK; 3038 } 3039 3040 3041 static void 3042 AsynchLaunchBinder(void (*func)(const entry_ref *, const BMessage *, bool on), 3043 const entry_ref *appRef, const BMessage *refs, bool openWithOK) 3044 { 3045 BMessage *task = new BMessage; 3046 task->AddPointer("function", (void *)func); 3047 task->AddMessage("refs", refs); 3048 task->AddBool("openWithOK", openWithOK); 3049 if (appRef != NULL) 3050 task->AddRef("appRef", appRef); 3051 3052 extern BLooper *gLaunchLooper; 3053 gLaunchLooper->PostMessage(task); 3054 } 3055 3056 static bool 3057 SniffIfGeneric(const entry_ref *ref) 3058 { 3059 BNode node(ref); 3060 char type[B_MIME_TYPE_LENGTH]; 3061 BNodeInfo info(&node); 3062 if (info.GetType(type) == B_OK 3063 && strcasecmp(type, B_FILE_MIME_TYPE) != 0) { 3064 // already has a type and it's not octet stream 3065 return false; 3066 } 3067 3068 BPath path(ref); 3069 if (path.Path()) { 3070 // force a mimeset 3071 node.RemoveAttr(kAttrMIMEType); 3072 update_mime_info(path.Path(), 0, 1, 1); 3073 } 3074 3075 return true; 3076 } 3077 3078 static void 3079 SniffIfGeneric(const BMessage *refs) 3080 { 3081 entry_ref ref; 3082 for (int32 index = 0; ; index++) { 3083 if (refs->FindRef("refs", index, &ref) != B_OK) 3084 break; 3085 SniffIfGeneric(&ref); 3086 } 3087 } 3088 3089 static void 3090 _TrackerLaunchAppWithDocuments(const entry_ref *appRef, const BMessage *refs, 3091 bool openWithOK) 3092 { 3093 team_id team; 3094 3095 status_t error = B_ERROR; 3096 BString alertString; 3097 3098 for (int32 mimesetIt = 0; ; mimesetIt++) { 3099 error = be_roster->Launch(appRef, refs, &team); 3100 if (error == B_ALREADY_RUNNING) 3101 // app already running, not really an error 3102 error = B_OK; 3103 3104 if (error == B_OK) 3105 break; 3106 3107 if (mimesetIt > 0) 3108 break; 3109 3110 // failed to open, try mimesetting the refs and launching again 3111 SniffIfGeneric(refs); 3112 } 3113 3114 if (error == B_OK) { 3115 // close possible parent window, if specified 3116 const node_ref *nodeToClose = 0; 3117 int32 numBytes; 3118 refs->FindData("nodeRefsToClose", B_RAW_TYPE, 3119 (const void **)&nodeToClose, &numBytes); 3120 if (nodeToClose) 3121 dynamic_cast<TTracker *>(be_app)->CloseParent(*nodeToClose); 3122 } else { 3123 alertString << "Could not open \"" << appRef->name << "\" (" 3124 << strerror(error) << "). "; 3125 if (refs && openWithOK && error != B_SHUTTING_DOWN) { 3126 alertString << kFindAlternativeStr; 3127 BAlert *alert = new BAlert("", alertString.String(), 3128 "Cancel", "Find", 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 3129 alert->SetShortcut(0, B_ESCAPE); 3130 if (alert->Go() == 1) 3131 error = TrackerOpenWith(refs); 3132 } else { 3133 BAlert *alert = new BAlert("", alertString.String(), 3134 "Cancel", 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 3135 alert->SetShortcut(0, B_ESCAPE); 3136 alert->Go(); 3137 } 3138 } 3139 } 3140 3141 extern "C" char** environ; 3142 3143 extern "C" status_t _kern_load_image(const char * const *flatArgs, 3144 size_t flatArgsSize, int32 argCount, int32 envCount, int32 priority, 3145 uint32 flags, port_id errorPort, uint32 errorToken); 3146 extern "C" status_t __flatten_process_args(const char * const *args, 3147 int32 argCount, const char * const *env, int32 envCount, char ***_flatArgs, 3148 size_t *_flatSize); 3149 3150 3151 static status_t 3152 LoaderErrorDetails(const entry_ref *app, BString &details) 3153 { 3154 BPath path; 3155 BEntry appEntry(app, true); 3156 3157 status_t result = appEntry.GetPath(&path); 3158 if (result != B_OK) 3159 return result; 3160 3161 char *argv[2] = { const_cast<char *>(path.Path()), 0}; 3162 3163 port_id errorPort = create_port(1, "Tracker loader error"); 3164 3165 // count environment variables 3166 uint32 envCount = 0; 3167 while (environ[envCount] != NULL) 3168 envCount++; 3169 3170 char** flatArgs = NULL; 3171 size_t flatArgsSize; 3172 result = __flatten_process_args((const char **)argv, 1, 3173 environ, envCount, &flatArgs, &flatArgsSize); 3174 if (result != B_OK) 3175 return result; 3176 3177 result = _kern_load_image(flatArgs, flatArgsSize, 1, envCount, 3178 B_NORMAL_PRIORITY, B_WAIT_TILL_LOADED, errorPort, 0); 3179 if (result == B_OK) { 3180 // we weren't supposed to be able to start the application... 3181 return B_ERROR; 3182 } 3183 3184 // read error message from port and construct details string 3185 3186 ssize_t bufferSize; 3187 3188 do { 3189 bufferSize = port_buffer_size_etc(errorPort, B_RELATIVE_TIMEOUT, 0); 3190 } while (bufferSize == B_INTERRUPTED); 3191 3192 if (bufferSize <= B_OK) { 3193 delete_port(errorPort); 3194 return bufferSize; 3195 } 3196 3197 uint8 *buffer = (uint8 *)malloc(bufferSize); 3198 if (buffer == NULL) { 3199 delete_port(errorPort); 3200 return B_NO_MEMORY; 3201 } 3202 3203 bufferSize = read_port_etc(errorPort, NULL, buffer, bufferSize, 3204 B_RELATIVE_TIMEOUT, 0); 3205 delete_port(errorPort); 3206 3207 if (bufferSize < B_OK) { 3208 free(buffer); 3209 return bufferSize; 3210 } 3211 3212 BMessage message; 3213 result = message.Unflatten((const char *)buffer); 3214 free(buffer); 3215 3216 if (result != B_OK) 3217 return result; 3218 3219 int32 errorCode = B_ERROR; 3220 result = message.FindInt32("error", &errorCode); 3221 if (result != B_OK) 3222 return result; 3223 3224 const char *detailName = NULL; 3225 switch (errorCode) { 3226 case B_MISSING_LIBRARY: 3227 detailName = "missing library"; 3228 break; 3229 3230 case B_MISSING_SYMBOL: 3231 detailName = "missing symbol"; 3232 break; 3233 } 3234 3235 if (detailName == NULL) 3236 return B_ERROR; 3237 3238 const char *detail; 3239 for (int32 i = 0; message.FindString(detailName, i, &detail) == B_OK; 3240 i++) { 3241 if (i > 0) 3242 details += ", "; 3243 details += detail; 3244 } 3245 3246 return B_OK; 3247 } 3248 3249 3250 static void 3251 _TrackerLaunchDocuments(const entry_ref */*doNotUse*/, const BMessage *refs, 3252 bool openWithOK) 3253 { 3254 BMessage copyOfRefs(*refs); 3255 3256 entry_ref documentRef; 3257 if (copyOfRefs.FindRef("refs", &documentRef) != B_OK) 3258 // nothing to launch, we are done 3259 return; 3260 3261 status_t error = B_ERROR; 3262 entry_ref app; 3263 BMessage *refsToPass = NULL; 3264 BString alertString; 3265 const char *alternative = 0; 3266 3267 for (int32 mimesetIt = 0; ; mimesetIt++) { 3268 alertString = ""; 3269 error = be_roster->FindApp(&documentRef, &app); 3270 3271 if (error != B_OK && mimesetIt == 0) { 3272 SniffIfGeneric(©OfRefs); 3273 continue; 3274 } 3275 3276 if (error != B_OK) { 3277 alertString << "Could not find an application to open \"" 3278 << documentRef.name << "\" (" << strerror(error) << "). "; 3279 if (openWithOK) 3280 alternative = kFindApplicationStr; 3281 3282 break; 3283 } else { 3284 BEntry appEntry(&app, true); 3285 for (int32 index = 0;;) { 3286 // remove the app itself from the refs received so we don't try 3287 // to open ourselves 3288 entry_ref ref; 3289 if (copyOfRefs.FindRef("refs", index, &ref) != B_OK) 3290 break; 3291 3292 // deal with symlinks properly 3293 BEntry documentEntry(&ref, true); 3294 if (appEntry == documentEntry) { 3295 PRINT(("stripping %s, app %s \n", ref.name, app.name)); 3296 copyOfRefs.RemoveData("refs", index); 3297 } else { 3298 PRINT(("leaving %s, app %s \n", ref.name, app.name)); 3299 index++; 3300 } 3301 } 3302 3303 refsToPass = CountRefs(©OfRefs) > 0 ? ©OfRefs: 0; 3304 team_id team; 3305 error = be_roster->Launch(&app, refsToPass, &team); 3306 if (error == B_ALREADY_RUNNING) 3307 // app already running, not really an error 3308 error = B_OK; 3309 if (error == B_OK || mimesetIt != 0) 3310 break; 3311 3312 SniffIfGeneric(©OfRefs); 3313 } 3314 } 3315 3316 if (error != B_OK && alertString.Length() == 0) { 3317 BString loaderErrorString; 3318 bool openedDocuments = true; 3319 3320 if (!refsToPass) { 3321 // we just double clicked the app itself, do not offer to 3322 // find a handling app 3323 openWithOK = false; 3324 openedDocuments = false; 3325 } 3326 3327 if (error == B_LAUNCH_FAILED_EXECUTABLE && !refsToPass) { 3328 alertString << "Could not open \"" << app.name 3329 << "\". The file is mistakenly marked as executable. "; 3330 3331 if (!openWithOK) { 3332 // offer the possibility to change the permissions 3333 3334 alertString << "\nShould this be fixed?"; 3335 BAlert *alert = new BAlert("", alertString.String(), 3336 "Cancel", "Proceed", 0, B_WIDTH_AS_USUAL, 3337 B_WARNING_ALERT); 3338 alert->SetShortcut(0, B_ESCAPE); 3339 if (alert->Go() == 1) { 3340 BEntry entry(&documentRef); 3341 mode_t permissions; 3342 3343 error = entry.GetPermissions(&permissions); 3344 if (error == B_OK) { 3345 error = entry.SetPermissions(permissions 3346 & ~(S_IXUSR | S_IXGRP | S_IXOTH)); 3347 } 3348 if (error == B_OK) { 3349 // we updated the permissions, so let's try again 3350 _TrackerLaunchDocuments(NULL, refs, false); 3351 return; 3352 } else { 3353 alertString = "Could not update permissions of " 3354 "file \""; 3355 alertString << app.name << "\". " << strerror(error); 3356 } 3357 } else 3358 return; 3359 } 3360 3361 alternative = kFindApplicationStr; 3362 } else if (error == B_LAUNCH_FAILED_APP_IN_TRASH) { 3363 alertString << "Could not open \"" << documentRef.name 3364 << "\" because application \"" << app.name << "\" is in the " 3365 "Trash. "; 3366 alternative = kFindAlternativeStr; 3367 } else if (error == B_LAUNCH_FAILED_APP_NOT_FOUND) { 3368 alertString << "Could not open \"" << documentRef.name << "\" " 3369 << "(" << strerror(error) << "). "; 3370 alternative = kFindAlternativeStr; 3371 } else if (error == B_MISSING_SYMBOL 3372 && LoaderErrorDetails(&app, loaderErrorString) == B_OK) { 3373 alertString << "Could not open \"" << documentRef.name << "\" "; 3374 if (openedDocuments) 3375 alertString << "with application \"" << app.name << "\" "; 3376 alertString << "(Missing symbol: " << loaderErrorString << "). \n"; 3377 alternative = kFindAlternativeStr; 3378 } else if (error == B_MISSING_LIBRARY 3379 && LoaderErrorDetails(&app, loaderErrorString) == B_OK) { 3380 alertString << "Could not open \"" << documentRef.name << "\" "; 3381 if (openedDocuments) 3382 alertString << "with application \"" << app.name << "\" "; 3383 alertString << "(Missing libraries: " << loaderErrorString 3384 << "). \n"; 3385 alternative = kFindAlternativeStr; 3386 } else { 3387 alertString << "Could not open \"" << documentRef.name 3388 << "\" with application \"" << app.name << "\" (" 3389 << strerror(error) << "). "; 3390 alternative = kFindAlternativeStr; 3391 } 3392 } 3393 3394 if (error != B_OK) { 3395 if (openWithOK) { 3396 ASSERT(alternative); 3397 alertString << alternative; 3398 BAlert *alert = new BAlert("", alertString.String(), 3399 "Cancel", "Find", 0, B_WIDTH_AS_USUAL, 3400 B_WARNING_ALERT); 3401 alert->SetShortcut(0, B_ESCAPE); 3402 if (alert->Go() == 1) 3403 error = TrackerOpenWith(refs); 3404 } else { 3405 BAlert *alert = new BAlert("", alertString.String(), 3406 "Cancel", 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 3407 alert->SetShortcut(0, B_ESCAPE); 3408 alert->Go(); 3409 } 3410 } 3411 } 3412 3413 // the following three calls don't return any reasonable error codes, 3414 // should fix that, making them void 3415 3416 status_t 3417 TrackerLaunch(const entry_ref *appRef, const BMessage *refs, bool async, 3418 bool openWithOK) 3419 { 3420 if (!async) 3421 _TrackerLaunchAppWithDocuments(appRef, refs, openWithOK); 3422 else { 3423 AsynchLaunchBinder(&_TrackerLaunchAppWithDocuments, appRef, refs, 3424 openWithOK); 3425 } 3426 3427 return B_OK; 3428 } 3429 3430 status_t 3431 TrackerLaunch(const entry_ref *appRef, bool async) 3432 { 3433 if (!async) 3434 _TrackerLaunchAppWithDocuments(appRef, 0, false); 3435 else 3436 AsynchLaunchBinder(&_TrackerLaunchAppWithDocuments, appRef, 0, false); 3437 3438 return B_OK; 3439 } 3440 3441 status_t 3442 TrackerLaunch(const BMessage *refs, bool async, bool openWithOK) 3443 { 3444 if (!async) 3445 _TrackerLaunchDocuments(0, refs, openWithOK); 3446 else 3447 AsynchLaunchBinder(&_TrackerLaunchDocuments, 0, refs, openWithOK); 3448 3449 return B_OK; 3450 } 3451 3452 status_t 3453 LaunchBrokenLink(const char *signature, const BMessage *refs) 3454 { 3455 // This call is to support a hacky workaround for double-clicking 3456 // broken refs for cifs 3457 be_roster->Launch(signature, const_cast<BMessage *>(refs)); 3458 return B_OK; 3459 } 3460 3461 // external launch calls; need to be robust, work if Tracker is not running 3462 3463 #if !B_BEOS_VERSION_DANO 3464 _IMPEXP_TRACKER 3465 #endif 3466 status_t 3467 FSLaunchItem(const entry_ref *application, const BMessage *refsReceived, 3468 bool async, bool openWithOK) 3469 { 3470 return TrackerLaunch(application, refsReceived, async, openWithOK); 3471 } 3472 3473 3474 #if !B_BEOS_VERSION_DANO 3475 _IMPEXP_TRACKER 3476 #endif 3477 status_t 3478 FSOpenWith(BMessage *listOfRefs) 3479 { 3480 status_t result = B_ERROR; 3481 listOfRefs->what = B_REFS_RECEIVED; 3482 3483 if (dynamic_cast<TTracker *>(be_app)) 3484 result = TrackerOpenWith(listOfRefs); 3485 else 3486 ASSERT(!"not yet implemented"); 3487 3488 return result; 3489 } 3490 3491 // legacy calls, need for compatibility 3492 3493 void 3494 FSOpenWithDocuments(const entry_ref *executable, BMessage *documents) 3495 { 3496 TrackerLaunch(executable, documents, true); 3497 delete documents; 3498 } 3499 3500 status_t 3501 FSLaunchUsing(const entry_ref *ref, BMessage *listOfRefs) 3502 { 3503 BMessage temp(B_REFS_RECEIVED); 3504 if (!listOfRefs) { 3505 ASSERT(ref); 3506 temp.AddRef("refs", ref); 3507 listOfRefs = &temp; 3508 } 3509 FSOpenWith(listOfRefs); 3510 return B_OK; 3511 } 3512 3513 status_t 3514 FSLaunchItem(const entry_ref *ref, BMessage* message, int32, bool async) 3515 { 3516 if (message) 3517 message->what = B_REFS_RECEIVED; 3518 3519 status_t result = TrackerLaunch(ref, message, async, true); 3520 delete message; 3521 return result; 3522 } 3523 3524 3525 void 3526 FSLaunchItem(const entry_ref *ref, BMessage *message, int32 workspace) 3527 { 3528 FSLaunchItem(ref, message, workspace, true); 3529 } 3530 3531 // Get the original path of an entry in the trash 3532 status_t 3533 FSGetOriginalPath(BEntry *entry, BPath *result) 3534 { 3535 status_t err; 3536 entry_ref ref; 3537 err = entry->GetRef(&ref); 3538 if (err != B_OK) 3539 return err; 3540 3541 // Only call the routine for entries in the trash 3542 if (!FSInTrashDir(&ref)) 3543 return B_ERROR; 3544 3545 BNode node(entry); 3546 BString originalPath; 3547 if (node.ReadAttrString(kAttrOriginalPath, &originalPath) == B_OK) { 3548 // We're in luck, the entry has the original path in an attribute 3549 err = result->SetTo(originalPath.String()); 3550 return err; 3551 } 3552 3553 // Iterate the parent directories to find one with 3554 // the original path attribute 3555 BEntry parent(*entry); 3556 err = parent.InitCheck(); 3557 if (err != B_OK) 3558 return err; 3559 3560 // walk up the directory structure until we find a node 3561 // with original path attribute 3562 do { 3563 // move to the parent of this node 3564 err = parent.GetParent(&parent); 3565 if (err != B_OK) 3566 return err; 3567 3568 // return if we are at the root of the trash 3569 if (FSIsTrashDir(&parent)) 3570 return B_ENTRY_NOT_FOUND; 3571 3572 // get the parent as a node 3573 err = node.SetTo(&parent); 3574 if (err != B_OK) 3575 return err; 3576 } while (node.ReadAttrString(kAttrOriginalPath, &originalPath) != B_OK); 3577 3578 // Found the attribute, figure out there this file 3579 // used to live, based on the successfully-read attribute 3580 err = result->SetTo(originalPath.String()); 3581 if (err != B_OK) 3582 return err; 3583 3584 BPath path, pathParent; 3585 err = parent.GetPath(&pathParent); 3586 if (err != B_OK) 3587 return err; 3588 err = entry->GetPath(&path); 3589 if (err != B_OK) 3590 return err; 3591 result->Append(path.Path() + strlen(pathParent.Path()) + 1); 3592 // compute the new path by appending the offset of 3593 // the item we are locating, to the original path 3594 // of the parent 3595 return B_OK; 3596 } 3597 3598 directory_which 3599 WellKnowEntryList::Match(const node_ref *node) 3600 { 3601 const WellKnownEntry *result = MatchEntry(node); 3602 if (result) 3603 return result->which; 3604 3605 return (directory_which)-1; 3606 } 3607 3608 const WellKnowEntryList::WellKnownEntry * 3609 WellKnowEntryList::MatchEntry(const node_ref *node) 3610 { 3611 if (!self) 3612 self = new WellKnowEntryList(); 3613 3614 return self->MatchEntryCommon(node); 3615 } 3616 3617 const WellKnowEntryList::WellKnownEntry * 3618 WellKnowEntryList::MatchEntryCommon(const node_ref *node) 3619 { 3620 uint32 count = entries.size(); 3621 for (uint32 index = 0; index < count; index++) 3622 if (*node == entries[index].node) 3623 return &entries[index]; 3624 3625 return NULL; 3626 } 3627 3628 3629 void 3630 WellKnowEntryList::Quit() 3631 { 3632 delete self; 3633 self = NULL; 3634 } 3635 3636 3637 void 3638 WellKnowEntryList::AddOne(directory_which which, const char *name) 3639 { 3640 BPath path; 3641 if (find_directory(which, &path, true) != B_OK) 3642 return; 3643 3644 BEntry entry(path.Path(), true); 3645 node_ref node; 3646 if (entry.GetNodeRef(&node) != B_OK) 3647 return; 3648 3649 entries.push_back(WellKnownEntry(&node, which, name)); 3650 } 3651 3652 3653 void 3654 WellKnowEntryList::AddOne(directory_which which, directory_which base, 3655 const char *extra, const char *name) 3656 { 3657 BPath path; 3658 if (find_directory(base, &path, true) != B_OK) 3659 return; 3660 3661 path.Append(extra); 3662 BEntry entry(path.Path(), true); 3663 node_ref node; 3664 if (entry.GetNodeRef(&node) != B_OK) 3665 return; 3666 3667 entries.push_back(WellKnownEntry(&node, which, name)); 3668 } 3669 3670 3671 void 3672 WellKnowEntryList::AddOne(directory_which which, const char *path, 3673 const char *name) 3674 { 3675 BEntry entry(path, true); 3676 node_ref node; 3677 if (entry.GetNodeRef(&node) != B_OK) 3678 return; 3679 3680 entries.push_back(WellKnownEntry(&node, which, name)); 3681 } 3682 3683 3684 WellKnowEntryList::WellKnowEntryList() 3685 { 3686 #ifdef __HAIKU__ 3687 AddOne(B_SYSTEM_DIRECTORY, "system"); 3688 #else 3689 AddOne(B_BEOS_DIRECTORY, "beos"); 3690 AddOne(B_BEOS_SYSTEM_DIRECTORY, "system"); 3691 #endif 3692 AddOne((directory_which)B_BOOT_DISK, "/boot", "boot"); 3693 AddOne(B_USER_DIRECTORY, "home"); 3694 3695 AddOne(B_BEOS_FONTS_DIRECTORY, "fonts"); 3696 AddOne(B_COMMON_FONTS_DIRECTORY, "fonts"); 3697 AddOne(B_USER_FONTS_DIRECTORY, "fonts"); 3698 3699 AddOne(B_BEOS_APPS_DIRECTORY, "apps"); 3700 AddOne(B_APPS_DIRECTORY, "apps"); 3701 AddOne((directory_which)B_USER_DESKBAR_APPS_DIRECTORY, 3702 B_USER_DESKBAR_DIRECTORY, "Applications", "apps"); 3703 3704 AddOne(B_BEOS_PREFERENCES_DIRECTORY, "preferences"); 3705 AddOne(B_PREFERENCES_DIRECTORY, "preferences"); 3706 AddOne((directory_which)B_USER_DESKBAR_PREFERENCES_DIRECTORY, 3707 B_USER_DESKBAR_DIRECTORY, "Preferences", "preferences"); 3708 3709 AddOne((directory_which)B_USER_MAIL_DIRECTORY, B_USER_DIRECTORY, "mail", 3710 "mail"); 3711 3712 AddOne((directory_which)B_USER_QUERIES_DIRECTORY, B_USER_DIRECTORY, 3713 "queries", "queries"); 3714 3715 3716 3717 AddOne(B_COMMON_DEVELOP_DIRECTORY, "develop"); 3718 AddOne((directory_which)B_USER_DESKBAR_DEVELOP_DIRECTORY, 3719 B_USER_DESKBAR_DIRECTORY, "Development", "develop"); 3720 3721 AddOne(B_USER_CONFIG_DIRECTORY, "config"); 3722 3723 AddOne((directory_which)B_USER_PEOPLE_DIRECTORY, B_USER_DIRECTORY, 3724 "people", "people"); 3725 3726 AddOne((directory_which)B_USER_DOWNLOADS_DIRECTORY, B_USER_DIRECTORY, 3727 "downloads", "downloads"); 3728 } 3729 3730 WellKnowEntryList *WellKnowEntryList::self = NULL; 3731 3732 } // namespace BPrivate 3733