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