#include <ctype.h>
#include <errno.h>
#include <strings.h>
#include <unistd.h>
#include <Alert.h>
#include <Application.h>
#include <Catalog.h>
#include <Debug.h>
#include <Directory.h>
#include <Entry.h>
#include <FindDirectory.h>
#include <Locale.h>
#include <NodeInfo.h>
#include <Path.h>
#include <Roster.h>
#include <Screen.h>
#include <String.h>
#include <StringFormat.h>
#include <SymLink.h>
#include <Volume.h>
#include <VolumeRoster.h>
#include <fs_attr.h>
#include <fs_info.h>
#include <sys/utsname.h>
#include <AutoLocker.h>
#include <libroot/libroot_private.h>
#include <system/syscalls.h>
#include <system/syscall_load_image.h>
#include "Attributes.h"
#include "Bitmaps.h"
#include "Commands.h"
#include "FindPanel.h"
#include "FSUndoRedo.h"
#include "FSUtils.h"
#include "InfoWindow.h"
#include "MimeTypes.h"
#include "OverrideAlert.h"
#include "StatusWindow.h"
#include "Thread.h"
#include "Tracker.h"
#include "TrackerSettings.h"
#include "Utilities.h"
#include "VirtualDirectoryManager.h"
enum {
kUserCanceled = B_ERRORS_END + 1,
kCopyCanceled = kUserCanceled,
kTrashCanceled
};
enum ConflictCheckResult {
kCanceled = kUserCanceled,
kPrompt,
kSkipAll,
kReplace,
kReplaceAll,
kNoConflicts
};
namespace BPrivate {
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "FSUtils"
static status_t FSDeleteFolder(BEntry*, CopyLoopControl*, bool updateStatus,
bool deleteTopDir = true, bool upateFileNameInStatus = false);
static status_t MoveEntryToTrash(BEntry*, BPoint*, Undo &undo);
static void LowLevelCopy(BEntry*, StatStruct*, BDirectory*, char* destName,
CopyLoopControl*, BPoint*);
status_t DuplicateTask(BObjectList<entry_ref, true>* srcList);
static status_t MoveTask(BObjectList<entry_ref, true>*, BEntry*, BList*, uint32);
static status_t _DeleteTask(BObjectList<entry_ref, true>*, bool);
static status_t _RestoreTask(BObjectList<entry_ref, true>*);
status_t CalcItemsAndSize(CopyLoopControl* loopControl,
BObjectList<entry_ref, true>* refList, ssize_t blockSize, int32* totalCount,
off_t* totalSize);
status_t MoveItem(BEntry* entry, BDirectory* destDir, BPoint* loc,
uint32 moveMode, const char* newName, Undo &undo,
CopyLoopControl* loopControl);
ConflictCheckResult PreFlightNameCheck(BObjectList<entry_ref, true>* srcList,
const BDirectory* destDir, int32* collisionCount, uint32 moveMode);
status_t CheckName(uint32 moveMode, const BEntry* srcEntry,
const BDirectory* destDir, bool multipleCollisions,
ConflictCheckResult &);
void CopyAttributes(CopyLoopControl* control, BNode* srcNode,
BNode* destNode, void* buffer, size_t bufsize);
void CopyPoseLocation(BNode* src, BNode* dest);
bool DirectoryMatchesOrContains(const BEntry*, directory_which);
bool DirectoryMatchesOrContains(const BEntry*, const char* additionalPath,
directory_which);
bool DirectoryMatches(const BEntry*, directory_which);
bool DirectoryMatches(const BEntry*, const char* additionalPath,
directory_which);
status_t empty_trash(void*);
static const char* kDeleteConfirmationStr =
B_TRANSLATE_MARK("Are you sure you want to delete the "
"selected item(s)? This operation cannot be reverted.");
static const char* kReplaceStr =
B_TRANSLATE_MARK("You are trying to replace the item:\n"
"\t%name%dest\n"
"with:\n"
"\t%name%src\n\n"
"Would you like to replace it with the one you are %movemode?");
static const char* kDirectoryReplaceStr =
B_TRANSLATE_MARK("An item named \"%name\" already exists in "
"this folder, and may contain\nitems with the same names. Would you like "
"to replace them with those contained in the folder you are %verb?");
static const char* kSymLinkReplaceStr =
B_TRANSLATE_MARK("An item named \"%name\" already exists in this "
"folder. Would you like to replace it with the symbolic link you are "
"creating?");
static const char* kNoFreeSpace =
B_TRANSLATE_MARK("Sorry, there is not enough free space on the "
"destination volume to copy the selection.");
static const char* kFileErrorString =
B_TRANSLATE_MARK("Error copying file \"%name\":\n\t%error\n\n"
"Would you like to continue?");
static const char* kFolderErrorString =
B_TRANSLATE_MARK("Error copying folder \"%name\":\n\t%error\n\n"
"Would you like to continue?");
static const char* kFileDeleteErrorString =
B_TRANSLATE_MARK("There was an error deleting \"%name\""
":\n\t%error");
static const char* kReplaceManyStr =
B_TRANSLATE_MARK("Some items already exist in this folder with "
"the same names as the items you are %verb.\n \nWould you like to "
"replace them with the ones you are %verb or be prompted for each "
"one?");
static const char* kFindAlternativeStr =
B_TRANSLATE_MARK("Would you like to find some other suitable "
"application?");
static const char* kFindApplicationStr =
B_TRANSLATE_MARK("Would you like to find a suitable application "
"to open the file?");
const char* kSkipAttributes[] = {
kAttrPoseInfo,
NULL
};
CopyLoopControl::~CopyLoopControl()
{
}
void
CopyLoopControl::Init(uint32 jobKind)
{
}
void
CopyLoopControl::Init(int32 totalItems, off_t totalSize,
const entry_ref* destDir, bool showCount)
{
}
bool
CopyLoopControl::FileError(const char* message, const char* name,
status_t error, bool allowContinue)
{
return false;
}
void
CopyLoopControl::UpdateStatus(const char* name, const entry_ref& ref,
int32 count, bool optional)
{
}
bool
CopyLoopControl::CheckUserCanceled()
{
return false;
}
CopyLoopControl::OverwriteMode
CopyLoopControl::OverwriteOnConflict(const BEntry* srcEntry,
const char* destName, const BDirectory* destDir, bool srcIsDir,
bool dstIsDir)
{
return kReplace;
}
bool
CopyLoopControl::SkipEntry(const BEntry*, bool)
{
return false;
}
void
CopyLoopControl::ChecksumChunk(const char*, size_t)
{
}
bool
CopyLoopControl::ChecksumFile(const entry_ref*)
{
return true;
}
bool
CopyLoopControl::SkipAttribute(const char*)
{
return false;
}
bool
CopyLoopControl::PreserveAttribute(const char*)
{
return false;
}
TrackerCopyLoopControl::TrackerCopyLoopControl()
:
fThread(find_thread(NULL)),
fSourceList(NULL)
{
}
TrackerCopyLoopControl::TrackerCopyLoopControl(uint32 jobKind)
:
fThread(find_thread(NULL)),
fSourceList(NULL)
{
Init(jobKind);
}
TrackerCopyLoopControl::TrackerCopyLoopControl(int32 totalItems,
off_t totalSize)
:
fThread(find_thread(NULL)),
fSourceList(NULL)
{
Init(totalItems, totalSize);
}
TrackerCopyLoopControl::~TrackerCopyLoopControl()
{
if (gStatusWindow != NULL)
gStatusWindow->RemoveStatusItem(fThread);
}
void
TrackerCopyLoopControl::Init(uint32 jobKind)
{
if (gStatusWindow != NULL)
gStatusWindow->CreateStatusItem(fThread, (StatusWindowState)jobKind);
}
void
TrackerCopyLoopControl::Init(int32 totalItems, off_t totalSize,
const entry_ref* destDir, bool showCount)
{
if (gStatusWindow != NULL) {
gStatusWindow->InitStatusItem(fThread, totalItems, totalSize,
destDir, showCount);
}
}
bool
TrackerCopyLoopControl::FileError(const char* message, const char* name,
status_t error, bool allowContinue)
{
BString buffer(message);
buffer.ReplaceFirst("%name", name);
buffer.ReplaceFirst("%error", strerror(error));
if (allowContinue) {
BAlert* alert = new BAlert("", buffer.String(), B_TRANSLATE("Cancel"),
B_TRANSLATE("OK"), 0, B_WIDTH_AS_USUAL, B_STOP_ALERT);
alert->SetShortcut(0, B_ESCAPE);
return alert->Go() != 0;
}
BAlert* alert = new BAlert("", buffer.String(), B_TRANSLATE("Cancel"), 0, 0,
B_WIDTH_AS_USUAL, B_STOP_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return false;
}
void
TrackerCopyLoopControl::UpdateStatus(const char* name, const entry_ref&,
int32 count, bool optional)
{
if (gStatusWindow != NULL)
gStatusWindow->UpdateStatus(fThread, name, count, optional);
}
bool
TrackerCopyLoopControl::CheckUserCanceled()
{
if (gStatusWindow == NULL)
return false;
if (gStatusWindow->CheckCanceledOrPaused(fThread))
return true;
if (fSourceList != NULL) {
}
return false;
}
bool
TrackerCopyLoopControl::SkipAttribute(const char* attributeName)
{
for (const char** skipAttribute = kSkipAttributes; *skipAttribute;
skipAttribute++) {
if (strcmp(*skipAttribute, attributeName) == 0)
return true;
}
return false;
}
void
TrackerCopyLoopControl::SetSourceList(EntryList* list)
{
fSourceList = list;
}
static BNode*
GetWritableNode(BEntry* entry, StatStruct* statBuf = 0)
{
StatStruct localStatbuf;
if (!statBuf) {
statBuf = &localStatbuf;
if (entry->GetStat(statBuf) != B_OK)
return 0;
}
if (S_ISREG(statBuf->st_mode))
return new BFile(entry, O_RDWR);
return new BNode(entry);
}
bool
CheckDevicesEqual(const entry_ref* srcRef, const Model* targetModel)
{
BDirectory destDir (targetModel->EntryRef());
struct stat deststat;
destDir.GetStat(&deststat);
return srcRef->device == deststat.st_dev;
}
status_t
FSSetPoseLocation(ino_t destDirInode, BNode* destNode, BPoint point)
{
PoseInfo poseInfo;
poseInfo.fInvisible = false;
poseInfo.fInitedDirectory = destDirInode;
poseInfo.fLocation = point;
ssize_t bytesWritten = destNode->WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0,
&poseInfo, sizeof(poseInfo));
if (bytesWritten == sizeof(poseInfo))
return B_OK;
else if (bytesWritten < 0)
return (status_t)bytesWritten;
else
return B_ERROR;
}
status_t
FSSetPoseLocation(BEntry* entry, BPoint point)
{
BNode node(entry);
status_t result = node.InitCheck();
if (result != B_OK)
return result;
BDirectory parent;
result = entry->GetParent(&parent);
if (result != B_OK)
return result;
node_ref destNodeRef;
result = parent.GetNodeRef(&destNodeRef);
if (result != B_OK)
return result;
return FSSetPoseLocation(destNodeRef.node, &node, point);
}
bool
FSGetPoseLocation(const BNode* node, BPoint* point)
{
PoseInfo poseInfo;
if (ReadAttr(node, kAttrPoseInfo, kAttrPoseInfoForeign,
B_RAW_TYPE, 0, &poseInfo, sizeof(poseInfo), &PoseInfo::EndianSwap)
== kReadAttrFailed) {
return false;
}
if (poseInfo.fInitedDirectory == -1LL)
return false;
*point = poseInfo.fLocation;
return true;
}
static void
SetupPoseLocation(ino_t sourceParentIno, ino_t destParentIno, const BNode* sourceNode,
BNode* destNode, BPoint* loc)
{
BPoint point;
if (loc == NULL
&& sourceParentIno != destParentIno
&& FSGetPoseLocation(sourceNode, &point)) {
loc = &point;
}
if (loc != NULL && loc != (BPoint*)-1) {
FSSetPoseLocation(destParentIno, destNode, *loc);
}
}
void
FSMoveToFolder(BObjectList<entry_ref, true>* srcList, BEntry* destEntry,
uint32 moveMode, BList* pointList)
{
if (srcList->IsEmpty()) {
delete srcList;
delete pointList;
delete destEntry;
return;
}
LaunchInNewThread("MoveTask", B_NORMAL_PRIORITY, MoveTask, srcList,
destEntry, pointList, moveMode);
}
void
FSDelete(entry_ref* ref, bool async, bool confirm)
{
BObjectList<entry_ref, true>* list = new BObjectList<entry_ref, true>(1);
list->AddItem(ref);
FSDeleteRefList(list, async, confirm);
}
void
FSDeleteRefList(BObjectList<entry_ref, true>* list, bool async, bool confirm)
{
if (async) {
LaunchInNewThread("DeleteTask", B_NORMAL_PRIORITY, _DeleteTask, list,
confirm);
} else
_DeleteTask(list, confirm);
}
void
FSRestoreRefList(BObjectList<entry_ref, true>* list, bool async)
{
if (async) {
LaunchInNewThread("RestoreTask", B_NORMAL_PRIORITY, _RestoreTask,
list);
} else
_RestoreTask(list);
}
void
FSMoveToTrash(BObjectList<entry_ref, true>* srcList, BList* pointList, bool async)
{
if (srcList->IsEmpty()) {
delete srcList;
delete pointList;
return;
}
if (async)
LaunchInNewThread("MoveTask", B_NORMAL_PRIORITY, MoveTask, srcList,
(BEntry*)0, pointList, kMoveSelectionTo);
else
MoveTask(srcList, 0, pointList, kMoveSelectionTo);
}
enum {
kNotConfirmed,
kConfirmedHomeMove,
kConfirmedAll
};
bool
ConfirmChangeIfWellKnownDirectory(const BEntry* entry, DestructiveAction action,
bool dontAsk, int32* confirmedAlready)
{
if (confirmedAlready && *confirmedAlready == kConfirmedAll)
return true;
if (FSIsDeskDir(entry) || FSIsPrintersDir(entry) || FSIsRootDir(entry) || FSIsTrashDir(entry))
return false;
if ((!DirectoryMatchesOrContains(entry, B_SYSTEM_DIRECTORY)
&& !DirectoryMatchesOrContains(entry, B_USER_DIRECTORY))
|| DirectoryMatchesOrContains(entry, B_SYSTEM_TEMP_DIRECTORY))
return true;
BString warning;
bool requireOverride = true;
if (DirectoryMatchesOrContains(entry, B_SYSTEM_DIRECTORY)) {
if (action == kRename) {
warning.SetTo(
B_TRANSLATE("If you rename the system folder or its "
"contents, you won't be able to boot %osName!\n\nAre you sure "
"you want to do this?\n\nTo rename the system folder or its "
"contents anyway, hold down the Shift key and click "
"\"Rename\"."));
} else if(action == kMove) {
warning.SetTo(
B_TRANSLATE("If you move the system folder or its "
"contents, you won't be able to boot %osName!\n\nAre you sure "
"you want to do this?\n\nTo move the system folder or its "
"contents anyway, hold down the Shift key and click "
"\"Move\"."));
} else {
warning.SetTo(
B_TRANSLATE("If you alter the system folder or its "
"contents, you won't be able to boot %osName!\n\nAre you sure "
"you want to do this?\n\nTo alter the system folder or its "
"contents anyway, hold down the Shift key and click "
"\"I know what I'm doing\"."));
}
} else if (DirectoryMatches(entry, B_USER_DIRECTORY)) {
if (action == kRename) {
warning .SetTo(
B_TRANSLATE("If you rename the home folder, %osName "
"may not behave properly!\n\nAre you sure you want to do this?"
"\n\nTo rename the home folder anyway, hold down the "
"Shift key and click \"Rename\"."));
} else if (action == kMove) {
warning .SetTo(
B_TRANSLATE("If you move the home folder, %osName "
"may not behave properly!\n\nAre you sure you want to do this?"
"\n\nTo move the home folder anyway, hold down the "
"Shift key and click \"Move\"."));
} else {
warning .SetTo(
B_TRANSLATE("If you alter the home folder, %osName "
"may not behave properly!\n\nAre you sure you want to do this?"
"\n\nTo alter the home folder anyway, hold down the "
"Shift key and click \"I know what I'm doing\"."));
}
} else if (DirectoryMatchesOrContains(entry, B_USER_CONFIG_DIRECTORY)
|| DirectoryMatchesOrContains(entry, B_SYSTEM_SETTINGS_DIRECTORY)) {
if (action == kRename) {
warning.SetTo(
B_TRANSLATE("If you rename %target, %osName may not behave "
"properly!\n\nAre you sure you want to do this?"));
} else if (action == kMove) {
warning.SetTo(
B_TRANSLATE("If you move %target, %osName may not behave "
"properly!\n\nAre you sure you want to do this?"));
} else {
warning.SetTo(
B_TRANSLATE("If you alter %target, %osName may not behave "
"properly!\n\nAre you sure you want to do this?"));
}
if (DirectoryMatchesOrContains(entry, "beos_mime",
B_USER_SETTINGS_DIRECTORY)
|| DirectoryMatchesOrContains(entry, "beos_mime",
B_SYSTEM_SETTINGS_DIRECTORY)) {
warning.ReplaceFirst("%target", B_TRANSLATE("the MIME settings"));
requireOverride = false;
} else if (DirectoryMatches(entry, B_USER_CONFIG_DIRECTORY)) {
warning.ReplaceFirst("%target", B_TRANSLATE("the config folder"));
requireOverride = false;
} else if (DirectoryMatches(entry, B_USER_SETTINGS_DIRECTORY)
|| DirectoryMatches(entry, B_SYSTEM_SETTINGS_DIRECTORY)) {
warning.ReplaceFirst("%target", B_TRANSLATE("the settings folder"));
requireOverride = false;
} else {
return true;
}
} else
return true;
if (dontAsk)
return false;
if (confirmedAlready && *confirmedAlready == kConfirmedHomeMove
&& !requireOverride)
return true;
struct utsname name;
if (uname(&name) == -1)
warning.ReplaceFirst("%osName", "Haiku");
else
warning.ReplaceFirst("%osName", name.sysname);
BString buttonLabel;
if (action == kRename) {
buttonLabel = B_TRANSLATE_COMMENT("Rename", "button label");
} else if (action == kMove) {
buttonLabel = B_TRANSLATE_COMMENT("Move", "button label");
} else {
buttonLabel = B_TRANSLATE_COMMENT("I know what I'm doing",
"button label");
}
OverrideAlert* alert = new OverrideAlert("", warning.String(),
buttonLabel.String(), (requireOverride ? B_SHIFT_KEY : 0),
B_TRANSLATE("Cancel"), 0, NULL, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetShortcut(1, B_ESCAPE);
if (alert->Go() == 1) {
if (confirmedAlready)
*confirmedAlready = kNotConfirmed;
return false;
}
if (confirmedAlready) {
if (!requireOverride)
*confirmedAlready = kConfirmedHomeMove;
else
*confirmedAlready = kConfirmedAll;
}
return true;
}
status_t
EditModelName(const Model* model, const char* name, size_t length)
{
if (model == NULL || name == NULL || name[0] == '\0' || length <= 0)
return B_BAD_VALUE;
BEntry entry(model->EntryRef());
status_t result = entry.InitCheck();
if (result != B_OK)
return result;
if (model->HasLocalizedName() || model->IsDesktop() || model->IsRoot()
|| model->IsTrash() || model->IsVirtualDirectory()) {
result = B_NOT_ALLOWED;
} else if (model->IsQuery()) {
BModelWriteOpener opener(const_cast<Model*>(model));
ASSERT(model->Node());
MoreOptionsStruct::SetQueryTemporary(model->Node(), false);
RenameUndo undo(entry, name);
result = entry.Rename(name);
if (result != B_OK)
undo.Remove();
} else if (model->IsVolume()) {
BVolume volume(model->NodeRef()->device);
result = volume.InitCheck();
if (result == B_OK && volume.IsReadOnly())
result = B_READ_ONLY_DEVICE;
if (result == B_OK) {
RenameVolumeUndo undo(volume, name);
result = volume.SetName(name);
if (result != B_OK)
undo.Remove();
}
} else {
BVolume volume(model->NodeRef()->device);
result = volume.InitCheck();
if (result == B_OK && volume.IsReadOnly())
result = B_READ_ONLY_DEVICE;
if (result == B_OK)
result = ShouldEditRefName(model->EntryRef(), name, length);
if (result == B_OK) {
RenameUndo undo(entry, name);
result = entry.Rename(name);
if (result != B_OK)
undo.Remove();
}
}
return result;
}
status_t
ShouldEditRefName(const entry_ref* ref, const char* name, size_t length)
{
if (ref == NULL || name == NULL || name[0] == '\0' || length <= 0)
return B_BAD_VALUE;
BEntry entry(ref);
if (entry.InitCheck() != B_OK)
return B_NO_INIT;
if (length >= B_FILE_NAME_LENGTH) {
BString text;
if (entry.IsDirectory())
text = B_TRANSLATE("The entered folder name is too long.");
else
text = B_TRANSLATE("The entered file name is too long.");
BAlert* alert = new BAlert("", text, B_TRANSLATE("OK"),
0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return B_NAME_TOO_LONG;
}
if (strcmp(name, ref->name) == 0)
return B_OK;
if (!ConfirmChangeIfWellKnownDirectory(&entry, kRename))
return B_CANCELED;
BDirectory parent;
if (entry.GetParent(&parent) != B_OK)
return B_ERROR;
if (parent.Contains(name)) {
BString text(B_TRANSLATE("An item named '%filename%' already exists."));
text.ReplaceFirst("%filename%", name);
BAlert* alert = new BAlert("", text, B_TRANSLATE("OK"),
0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return B_NAME_IN_USE;
}
return B_OK;
}
static status_t
InitCopy(CopyLoopControl* loopControl, uint32 moveMode,
BObjectList<entry_ref, true>* srcList, BVolume* dstVol, BDirectory* destDir,
entry_ref* destRef, bool preflightNameCheck, bool needSizeCalculation,
int32* collisionCount, ConflictCheckResult* preflightResult)
{
if (dstVol->IsReadOnly())
return B_READ_ONLY_DEVICE;
int32 numItems = srcList->CountItems();
int32 askOnceOnly = kNotConfirmed;
for (int32 index = 0; index < numItems; index++) {
BEntry entry((entry_ref*)srcList->ItemAt(index));
if (FSIsRootDir(&entry)) {
BString errorStr;
if (moveMode == kCreateLink || moveMode == kCreateRelativeLink)
errorStr.SetTo(B_TRANSLATE("You cannot create a link to the root directory."));
else
errorStr.SetTo(B_TRANSLATE("You cannot copy or move the root directory."));
BAlert* alert = new BAlert("", errorStr.String(), B_TRANSLATE("Cancel"), 0, 0,
B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return B_ERROR;
} else if (FSIsTrashDir(&entry)) {
BString errorStr;
if (moveMode == kCreateLink || moveMode == kCreateRelativeLink)
errorStr.SetTo(B_TRANSLATE("You cannot create a link to the Trash directory."));
else
errorStr.SetTo(B_TRANSLATE("You cannot copy or move the Trash directory."));
BAlert* alert = new BAlert("", errorStr.String(), B_TRANSLATE("Cancel"), 0, 0,
B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return B_ERROR;
} else if (FSIsPrintersDir(&entry)
&& (moveMode == kCopySelectionTo || moveMode == kMoveSelectionTo)) {
BString errorStr(B_TRANSLATE("You cannot copy or move the Printers directory."));
BAlert* alert = new BAlert("", errorStr.String(), B_TRANSLATE("Cancel"), 0, 0,
B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return B_ERROR;
}
if (moveMode == kMoveSelectionTo
&& !ConfirmChangeIfWellKnownDirectory(&entry, kMove, false, &askOnceOnly)) {
return B_ERROR;
}
}
if (preflightNameCheck) {
ASSERT(collisionCount);
ASSERT(preflightResult);
*preflightResult = kPrompt;
*collisionCount = 0;
*preflightResult = PreFlightNameCheck(srcList, destDir,
collisionCount, moveMode);
if (*preflightResult == kCanceled) {
return B_ERROR;
}
}
switch (moveMode) {
case kCopySelectionTo:
case kDuplicateSelection:
case kMoveSelectionTo:
{
loopControl->Init(moveMode == kMoveSelectionTo ? kMoveState
: kCopyState);
int32 totalItems = 0;
off_t totalSize = 0;
if (needSizeCalculation) {
if (CalcItemsAndSize(loopControl, srcList,
dstVol->BlockSize(), &totalItems, &totalSize)
!= B_OK) {
return B_ERROR;
}
if ((totalSize + (4* kKBSize)) >= dstVol->FreeBytes()) {
BAlert* alert = new BAlert("",
B_TRANSLATE_NOCOLLECT(kNoFreeSpace),
B_TRANSLATE("Cancel"),
0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return B_ERROR;
}
}
loopControl->Init(totalItems, totalSize, destRef);
break;
}
case kCreateLink:
if (numItems > 10) {
loopControl->Init(kCreateLinkState);
loopControl->Init(numItems, numItems, destRef);
}
break;
}
return B_OK;
}
bool
delete_ref(void* ref)
{
delete (entry_ref*)ref;
return false;
}
bool
delete_point(void* point)
{
delete (BPoint*)point;
return false;
}
static status_t
MoveTask(BObjectList<entry_ref, true>* srcList, BEntry* destEntry, BList* pointList, uint32 moveMode)
{
ASSERT(!srcList->IsEmpty());
dev_t srcVolumeDevice = srcList->FirstItem()->device;
dev_t destVolumeDevice = srcVolumeDevice;
StatStruct deststat;
BVolume volume(srcVolumeDevice);
entry_ref destRef;
bool destIsTrash = false;
BDirectory destDir;
BDirectory* destDirToCheck = NULL;
bool needPreflightNameCheck = false;
bool sourceIsReadOnly = volume.IsReadOnly();
volume.Unset();
bool fromUndo = FSIsUndoMoveMode(moveMode);
moveMode = FSMoveMode(moveMode);
if (destEntry != NULL) {
destEntry->GetRef(&destRef);
destDir.SetTo(destEntry);
destDir.GetStat(&deststat);
destDirToCheck = &destDir;
destVolumeDevice = deststat.st_dev;
destIsTrash = FSIsTrashDir(destEntry);
volume.SetTo(destVolumeDevice);
needPreflightNameCheck = true;
} else if (moveMode == kDuplicateSelection) {
BEntry entry;
entry.SetTo(srcList->FirstItem());
entry.GetParent(&destDir);
volume.SetTo(srcVolumeDevice);
} else {
destIsTrash = true;
FSGetTrashDir(&destDir, srcVolumeDevice);
volume.SetTo(srcVolumeDevice);
BEntry entry;
destDir.GetEntry(&entry);
destDirToCheck = &destDir;
entry.GetRef(&destRef);
}
if (moveMode == kCopySelectionTo && destIsTrash) {
moveMode = kMoveSelectionTo;
}
if (moveMode == kMoveSelectionTo && sourceIsReadOnly)
moveMode = kCopySelectionTo;
bool needSizeCalculation = true;
if ((moveMode == kMoveSelectionTo && srcVolumeDevice == destVolumeDevice)
|| destIsTrash) {
needSizeCalculation = false;
}
MoveCopyUndo undo(srcList, destDir, pointList, moveMode);
if (fromUndo)
undo.Remove();
TrackerCopyLoopControl loopControl;
ConflictCheckResult conflictCheckResult = kPrompt;
int32 collisionCount = 0;
status_t result = InitCopy(&loopControl, moveMode, srcList,
&volume, destDirToCheck, &destRef, needPreflightNameCheck,
needSizeCalculation, &collisionCount, &conflictCheckResult);
loopControl.SetSourceList(srcList);
if (result == B_OK) {
for (int32 i = 0; i < srcList->CountItems(); i++) {
BPoint* loc = (BPoint*)-1;
entry_ref* srcRef = srcList->ItemAt(i);
if (moveMode == kDuplicateSelection) {
BEntry entry(srcRef);
entry.GetParent(&destDir);
destDir.GetStat(&deststat);
volume.SetTo(srcRef->device);
}
if (moveMode != kCreateLink
&& moveMode != kCreateRelativeLink
&& moveMode != kDuplicateSelection
&& !destIsTrash
&& (srcRef->device == destRef.device
&& srcRef->directory == deststat.st_ino)) {
continue;
}
if (loopControl.CheckUserCanceled())
break;
BEntry sourceEntry(srcRef);
if (sourceEntry.InitCheck() != B_OK) {
BString error(B_TRANSLATE("Error moving \"%name\"."));
error.ReplaceFirst("%name", srcRef->name);
BAlert* alert = new BAlert("", error.String(),
B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL,
B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
break;
}
if (destIsTrash) {
if (pointList != NULL)
loc = (BPoint*)pointList->ItemAt(i);
result = MoveEntryToTrash(&sourceEntry, loc, undo);
if (result != B_OK) {
BString error(B_TRANSLATE("Error moving \"%name\" to Trash. "
"(%error)"));
error.ReplaceFirst("%name", srcRef->name);
error.ReplaceFirst("%error", strerror(result));
BAlert* alert = new BAlert("", error.String(),
B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL,
B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
break;
}
continue;
}
if (CheckName(moveMode, &sourceEntry, &destDir,
collisionCount > 1, conflictCheckResult) != B_OK) {
loopControl.UpdateStatus(srcRef->name, *srcRef, 1);
continue;
}
if (pointList != NULL && moveMode != kCopySelectionTo) {
loc = (BPoint*)pointList->ItemAt(i);
BNode* sourceNode = GetWritableNode(&sourceEntry);
if (sourceNode != NULL && sourceNode->InitCheck() == B_OK)
FSSetPoseLocation(deststat.st_ino, sourceNode, *loc);
delete sourceNode;
}
if (pointList != NULL)
loc = (BPoint*)pointList->ItemAt(i);
result = MoveItem(&sourceEntry, &destDir, loc, moveMode, NULL, undo, &loopControl);
if (result != B_OK)
break;
}
}
delete srcList;
delete destEntry;
if (pointList != NULL) {
pointList->DoForEach(delete_point);
delete pointList;
}
return B_OK;
}
class FailWithAlert {
public:
static void FailOnError(status_t error, const char* string,
const char* name = NULL)
{
if (error != B_OK)
throw FailWithAlert(error, string, name);
}
FailWithAlert(status_t error, const char* string, const char* name)
:
fString(string),
fName(name),
fError(error)
{
}
const char* fString;
const char* fName;
status_t fError;
};
class MoveError {
public:
static void FailOnError(status_t error)
{
if (error != B_OK)
throw MoveError(error);
}
MoveError(status_t error)
:
fError(error)
{
}
status_t fError;
};
void
CopyFile(BEntry* srcFile, StatStruct* srcStat, BDirectory* destDir,
CopyLoopControl* loopControl, BPoint* loc, bool makeOriginalName,
Undo &undo)
{
if (loopControl->SkipEntry(srcFile, true))
return;
node_ref node;
destDir->GetNodeRef(&node);
BVolume volume(node.device);
if ((srcStat->st_size + kKBSize) >= volume.FreeBytes()) {
loopControl->FileError(B_TRANSLATE_NOCOLLECT(kNoFreeSpace), "",
B_DEVICE_FULL, false);
throw (status_t)B_DEVICE_FULL;
}
char destName[B_FILE_NAME_LENGTH];
srcFile->GetName(destName);
entry_ref ref;
srcFile->GetRef(&ref);
loopControl->UpdateStatus(destName, ref, 1024, true);
if (makeOriginalName) {
BString suffix(" ");
suffix << B_TRANSLATE_COMMENT("copy", "filename copy"),
FSMakeOriginalName(destName, destDir, suffix.String());
undo.UpdateEntry(srcFile, destName);
}
BEntry conflictingEntry;
if (destDir->FindEntry(destName, &conflictingEntry) == B_OK) {
switch (loopControl->OverwriteOnConflict(srcFile, destName, destDir,
false, false)) {
case TrackerCopyLoopControl::kSkip:
return;
case TrackerCopyLoopControl::kReplace:
if (!conflictingEntry.IsDirectory()) {
ThrowOnError(conflictingEntry.Remove());
break;
}
case TrackerCopyLoopControl::kMerge:
break;
}
}
try {
LowLevelCopy(srcFile, srcStat, destDir, destName, loopControl, loc);
} catch (status_t err) {
if (err == kCopyCanceled)
throw (status_t)err;
if (err != B_OK) {
if (!loopControl->FileError(
B_TRANSLATE_NOCOLLECT(kFileErrorString), destName, err,
true)) {
throw (status_t)err;
} else {
loopControl->UpdateStatus(NULL, ref, (int32)srcStat->st_size);
}
}
}
}
#ifdef _SILENTLY_CORRECT_FILE_NAMES
static bool
CreateFileSystemCompatibleName(const BDirectory* destDir, char* destName)
{
BEntry target;
destDir->GetEntry(&target);
entry_ref targetRef;
fs_info info;
if (target.GetRef(&targetRef) == B_OK
&& fs_stat_dev(targetRef.device, &info) == B_OK
&& !strcmp(info.fsh_name, "fat")) {
bool wasInvalid = false;
int32 length = strlen(destName) - 1;
while (destName[length] == '.') {
destName[length--] = '\0';
wasInvalid = true;
}
char* invalid = destName;
while ((invalid = strpbrk(invalid, "?<>\\:\"|*")) != NULL) {
invalid[0] = '_';
wasInvalid = true;
}
return wasInvalid;
}
return false;
}
#endif
static void
LowLevelCopy(BEntry* srcEntry, StatStruct* srcStat, BDirectory* destDir,
char* destName, CopyLoopControl* loopControl, BPoint* loc)
{
entry_ref ref;
ThrowOnError(srcEntry->GetRef(&ref));
if (S_ISLNK(srcStat->st_mode)) {
BSymLink srcLink;
BSymLink newLink;
char linkpath[MAXPATHLEN];
ThrowOnError(srcLink.SetTo(srcEntry));
ssize_t size = srcLink.ReadLink(linkpath, MAXPATHLEN - 1);
if (size < 0)
ThrowOnError(size);
ThrowOnError(destDir->CreateSymLink(destName, linkpath, &newLink));
node_ref destNodeRef;
destDir->GetNodeRef(&destNodeRef);
SetupPoseLocation(ref.directory, destNodeRef.node, &srcLink,
&newLink, loc);
BNodeInfo nodeInfo(&newLink);
nodeInfo.SetType(B_LINK_MIMETYPE);
newLink.SetPermissions(srcStat->st_mode);
newLink.SetOwner(srcStat->st_uid);
newLink.SetGroup(srcStat->st_gid);
newLink.SetModificationTime(srcStat->st_mtime);
newLink.SetCreationTime(srcStat->st_crtime);
return;
}
BFile srcFile(srcEntry, O_RDONLY);
ThrowOnInitCheckError(&srcFile);
const size_t kMinBufferSize = 1024* 128;
const size_t kMaxBufferSize = 1024* 1024;
size_t bufsize = kMinBufferSize;
if ((off_t)bufsize < srcStat->st_size) {
system_info sinfo;
get_system_info(&sinfo);
size_t freesize = static_cast<size_t>(
(sinfo.max_pages - sinfo.used_pages) * B_PAGE_SIZE);
bufsize = freesize / 4;
bufsize -= bufsize % (16* 1024);
if (bufsize < kMinBufferSize) {
bufsize = kMinBufferSize;
} else if (bufsize > kMaxBufferSize) {
bufsize = kMaxBufferSize;
}
}
BFile destFile(destDir, destName, O_RDWR | O_CREAT);
#ifdef _SILENTLY_CORRECT_FILE_NAMES
if ((destFile.InitCheck() == B_BAD_VALUE
|| destFile.InitCheck() == B_NOT_ALLOWED)
&& CreateFileSystemCompatibleName(destDir, destName)) {
destFile.SetTo(destDir, destName, B_CREATE_FILE | B_READ_WRITE);
}
#endif
ThrowOnInitCheckError(&destFile);
node_ref destNodeRef;
destDir->GetNodeRef(&destNodeRef);
SetupPoseLocation(ref.directory, destNodeRef.node, &srcFile,
&destFile, loc);
char* buffer = new char[bufsize];
try {
while (true) {
if (loopControl->CheckUserCanceled()) {
destFile.Unset();
BEntry destEntry;
if (destDir->FindEntry(destName, &destEntry) == B_OK)
destEntry.Remove();
throw (status_t)kCopyCanceled;
}
ASSERT(buffer);
ssize_t bytes = srcFile.Read(buffer, bufsize);
if (bytes > 0) {
ssize_t updateBytes = 0;
if (bytes > 32* 1024) {
updateBytes = bytes / 2;
loopControl->UpdateStatus(NULL, ref, updateBytes, true);
}
loopControl->ChecksumChunk(buffer, (size_t)bytes);
ssize_t result = destFile.Write(buffer, (size_t)bytes);
if (result != bytes) {
if (result < 0)
throw (status_t)result;
throw (status_t)B_ERROR;
}
result = destFile.Sync();
if (result != B_OK)
throw (status_t)result;
loopControl->UpdateStatus(NULL, ref, bytes - updateBytes,
true);
} else if (bytes < 0) {
throw (status_t)bytes;
} else {
break;
}
}
CopyAttributes(loopControl, &srcFile, &destFile, buffer, bufsize);
} catch (...) {
delete[] buffer;
throw;
}
destFile.SetPermissions(srcStat->st_mode);
destFile.SetOwner(srcStat->st_uid);
destFile.SetGroup(srcStat->st_gid);
destFile.SetModificationTime(srcStat->st_mtime);
destFile.SetCreationTime(srcStat->st_crtime);
delete[] buffer;
if (!loopControl->ChecksumFile(&ref)) {
destFile.Unset();
BEntry destEntry;
if (destDir->FindEntry(destName, &destEntry) == B_OK)
destEntry.Remove();
throw (status_t)kUserCanceled;
}
}
void
CopyAttributes(CopyLoopControl* control, BNode* srcNode, BNode* destNode,
void* buffer, size_t bufsize)
{
srcNode->RewindAttrs();
char name[256];
while (srcNode->GetNextAttrName(name) == B_OK) {
if (control->SkipAttribute(name))
continue;
attr_info info;
if (srcNode->GetAttrInfo(name, &info) != B_OK)
continue;
if (control->PreserveAttribute(name)) {
attr_info dest_info;
if (destNode->GetAttrInfo(name, &dest_info) == B_OK)
continue;
}
if (info.size == 0)
destNode->WriteAttr(name, info.type, 0, buffer, 0);
ssize_t bytes;
ssize_t numToRead = (ssize_t)info.size;
for (off_t offset = 0; numToRead > 0; offset += bytes) {
size_t chunkSize = (size_t)numToRead;
if (chunkSize > bufsize)
chunkSize = bufsize;
bytes = srcNode->ReadAttr(name, info.type, offset,
buffer, chunkSize);
if (bytes <= 0)
break;
destNode->WriteAttr(name, info.type, offset, buffer,
(size_t)bytes);
numToRead -= bytes;
}
}
}
static void
CopyFolder(BEntry* srcEntry, BDirectory* destDir,
CopyLoopControl* loopControl, BPoint* loc, bool makeOriginalName,
Undo &undo, bool removeSource = false)
{
BDirectory newDir;
BEntry entry;
status_t err = B_OK;
bool createDirectory = true;
BEntry existingEntry;
if (loopControl->SkipEntry(srcEntry, false))
return;
entry_ref ref;
srcEntry->GetRef(&ref);
char destName[B_FILE_NAME_LENGTH];
strlcpy(destName, ref.name, sizeof(destName));
loopControl->UpdateStatus(ref.name, ref, 1024, true);
if (makeOriginalName) {
BString suffix(" ");
suffix << B_TRANSLATE_COMMENT("copy", "filename copy"),
FSMakeOriginalName(destName, destDir, suffix.String());
undo.UpdateEntry(srcEntry, destName);
}
if (destDir->FindEntry(destName, &existingEntry) == B_OK) {
bool isDirectory = existingEntry.IsDirectory();
switch (loopControl->OverwriteOnConflict(srcEntry, destName, destDir,
true, isDirectory)) {
case TrackerCopyLoopControl::kSkip:
return;
case TrackerCopyLoopControl::kReplace:
if (!isDirectory) {
ThrowOnError(existingEntry.Remove());
break;
}
case TrackerCopyLoopControl::kMerge:
ASSERT(isDirectory);
newDir.SetTo(&existingEntry);
createDirectory = false;
break;
}
}
BDirectory srcDir(srcEntry);
srcDir.Rewind();
if (createDirectory) {
err = destDir->CreateDirectory(destName, &newDir);
#ifdef _SILENTLY_CORRECT_FILE_NAMES
if (err == B_BAD_VALUE) {
if (CreateFileSystemCompatibleName(destDir, destName))
err = destDir->CreateDirectory(destName, &newDir);
}
#endif
if (err != B_OK) {
if (!loopControl->FileError(B_TRANSLATE_NOCOLLECT(
kFolderErrorString), destName, err, true)) {
throw err;
}
return;
}
}
char* buffer;
if (createDirectory && err == B_OK
&& (buffer = (char*)malloc(32768)) != 0) {
CopyAttributes(loopControl, &srcDir, &newDir, buffer, 32768);
free(buffer);
}
StatStruct statbuf;
srcDir.GetStat(&statbuf);
dev_t sourceDeviceID = statbuf.st_dev;
node_ref destNodeRef;
destDir->GetNodeRef(&destNodeRef);
SetupPoseLocation(ref.directory, destNodeRef.node, &srcDir,
&newDir, loc);
while (srcDir.GetNextEntry(&entry) == B_OK) {
if (loopControl->CheckUserCanceled())
throw (status_t)kUserCanceled;
entry.GetStat(&statbuf);
if (S_ISDIR(statbuf.st_mode)) {
if (statbuf.st_dev != sourceDeviceID) {
PRINT(("Avoiding mount point %" B_PRIdDEV ", %" B_PRIdDEV "\n",
statbuf.st_dev, sourceDeviceID));
continue;
}
CopyFolder(&entry, &newDir, loopControl, 0, false, undo,
removeSource);
if (removeSource)
FSDeleteFolder(&entry, loopControl, true, true, false);
} else if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) {
CopyFile(&entry, &statbuf, &newDir, loopControl, 0, false, undo);
if (removeSource)
entry.Remove();
} else {
}
}
if (removeSource)
srcEntry->Remove();
else
srcEntry->Unset();
}
status_t
RecursiveMove(BEntry* entry, BDirectory* destDir, CopyLoopControl* loopControl)
{
const char* name = entry->Name();
if (destDir->Contains(name)) {
BPath path (destDir, name);
BDirectory subDir (path.Path());
entry_ref ref;
entry->GetRef(&ref);
BDirectory source(&ref);
if (source.InitCheck() == B_OK) {
source.Rewind();
BEntry current;
while (source.GetNextEntry(¤t) == B_OK) {
if (current.IsDirectory()) {
RecursiveMove(¤t, &subDir, loopControl);
current.Remove();
} else {
name = current.Name();
if (loopControl->OverwriteOnConflict(¤t, name,
&subDir, true, false)
!= TrackerCopyLoopControl::kSkip) {
MoveError::FailOnError(current.MoveTo(&subDir,
NULL, true));
}
}
}
}
entry->Remove();
} else
MoveError::FailOnError(entry->MoveTo(destDir));
return B_OK;
}
status_t
MoveItem(BEntry* entry, BDirectory* destDir, BPoint* loc, uint32 moveMode,
const char* newName, Undo &undo, CopyLoopControl* loopControl)
{
entry_ref ref;
try {
node_ref destNode;
StatStruct statbuf;
MoveError::FailOnError(entry->GetStat(&statbuf));
MoveError::FailOnError(entry->GetRef(&ref));
MoveError::FailOnError(destDir->GetNodeRef(&destNode));
if (moveMode == kCreateLink || moveMode == kCreateRelativeLink) {
PoseInfo poseInfo;
char name[B_FILE_NAME_LENGTH];
strlcpy(name, ref.name, sizeof(name));
BSymLink link;
BString suffix(" ");
suffix << B_TRANSLATE_COMMENT("link", "filename link"),
FSMakeOriginalName(name, destDir, suffix.String());
undo.UpdateEntry(entry, name);
BPath path;
entry->GetPath(&path);
if (loc != NULL && loc != (BPoint*)-1) {
poseInfo.fInvisible = false;
poseInfo.fInitedDirectory = destNode.node;
poseInfo.fLocation = *loc;
}
status_t err = B_ERROR;
if (moveMode == kCreateRelativeLink) {
if (statbuf.st_dev == destNode.device) {
char oldwd[B_PATH_NAME_LENGTH];
getcwd(oldwd, B_PATH_NAME_LENGTH);
BEntry destEntry;
destDir->GetEntry(&destEntry);
BPath destPath;
destEntry.GetPath(&destPath);
chdir(destPath.Path());
BString destString(destPath.Path());
destString.Append("/");
BString srcString(path.Path());
srcString.RemoveLast(path.Leaf());
const char* src = srcString.String();
const char* dest = destString.String();
const char* lastFolderSrc = src;
const char* lastFolderDest = dest;
while (*src && *dest && *src == *dest) {
++src;
if (*dest++ == '/') {
lastFolderSrc = src;
lastFolderDest = dest;
}
}
src = lastFolderSrc;
dest = lastFolderDest;
BString source;
if (*dest == '\0' && *src != '\0') {
source.Append(src);
} else if (*dest != '\0') {
while (*dest) {
if (*dest == '/')
source.Prepend("../");
++dest;
}
source.Append(src);
}
source.Append(path.Leaf());
err = destDir->CreateSymLink(name, source.String(),
&link);
chdir(oldwd);
} else
moveMode = kCreateLink;
}
if (moveMode == kCreateLink)
err = destDir->CreateSymLink(name, path.Path(), &link);
if (err == B_UNSUPPORTED) {
throw FailWithAlert(err,
B_TRANSLATE("The target disk does not support "
"creating links."), NULL);
}
FailWithAlert::FailOnError(err,
B_TRANSLATE("Error creating link to \"%name\"."),
ref.name);
if (loc != NULL && loc != (BPoint*)-1)
link.WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &poseInfo, sizeof(PoseInfo));
BNodeInfo nodeInfo(&link);
nodeInfo.SetType(B_LINK_MIMETYPE);
return B_OK;
}
if (statbuf.st_dev == destNode.device && moveMode != kCopySelectionTo
&& moveMode != kDuplicateSelection) {
loopControl->UpdateStatus(ref.name, ref, 1);
if (entry->IsDirectory())
return RecursiveMove(entry, destDir, loopControl);
MoveError::FailOnError(entry->MoveTo(destDir, newName));
} else {
bool makeOriginalName = (moveMode == kDuplicateSelection);
if (S_ISDIR(statbuf.st_mode)) {
CopyFolder(entry, destDir, loopControl, loc, makeOriginalName,
undo, moveMode == kMoveSelectionTo);
} else {
CopyFile(entry, &statbuf, destDir, loopControl, loc,
makeOriginalName, undo);
if (moveMode == kMoveSelectionTo)
entry->Remove();
}
}
} catch (status_t error) {
return error;
} catch (MoveError& error) {
BString errorString(B_TRANSLATE("Error moving \"%name\""));
errorString.ReplaceFirst("%name", ref.name);
BAlert* alert = new BAlert("", errorString.String(), B_TRANSLATE("OK"),
0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return error.fError;
} catch (FailWithAlert& error) {
BString buffer(error.fString);
if (error.fName != NULL)
buffer.ReplaceFirst("%name", error.fName);
else
buffer << error.fString;
BAlert* alert = new BAlert("", buffer.String(), B_TRANSLATE("OK"),
0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return error.fError;
}
return B_OK;
}
void
FSDuplicate(BObjectList<entry_ref, true>* srcList, BList* pointList)
{
LaunchInNewThread("DupTask", B_NORMAL_PRIORITY, MoveTask, srcList,
(BEntry*)NULL, pointList, kDuplicateSelection);
}
#if 0
status_t
FSCopyFolder(BEntry* srcEntry, BDirectory* destDir,
CopyLoopControl* loopControl, BPoint* loc, bool makeOriginalName)
{
try
CopyFolder(srcEntry, destDir, loopControl, loc, makeOriginalName);
catch (status_t error) {
return error;
return B_OK;
}
#endif
status_t
FSCopyAttributesAndStats(BNode* srcNode, BNode* destNode, bool copyTimes)
{
char* buffer = new char[1024];
srcNode->RewindAttrs();
char name[256];
while (srcNode->GetNextAttrName(name) == B_OK) {
attr_info info;
if (srcNode->GetAttrInfo(name, &info) != B_OK)
continue;
attr_info dest_info;
if (destNode->GetAttrInfo(name, &dest_info) == B_OK)
continue;
ssize_t bytes;
ssize_t numToRead = (ssize_t)info.size;
for (off_t offset = 0; numToRead > 0; offset += bytes) {
size_t chunkSize = (size_t)numToRead;
if (chunkSize > 1024)
chunkSize = 1024;
bytes = srcNode->ReadAttr(name, info.type, offset, buffer,
chunkSize);
if (bytes <= 0)
break;
destNode->WriteAttr(name, info.type, offset, buffer,
(size_t)bytes);
numToRead -= bytes;
}
}
delete[] buffer;
struct stat srcStat;
srcNode->GetStat(&srcStat);
destNode->SetPermissions(srcStat.st_mode);
destNode->SetOwner(srcStat.st_uid);
destNode->SetGroup(srcStat.st_gid);
if (copyTimes) {
destNode->SetModificationTime(srcStat.st_mtime);
destNode->SetCreationTime(srcStat.st_crtime);
}
return B_OK;
}
#if 0
status_t
FSCopyFile(BEntry* srcFile, StatStruct* srcStat, BDirectory* destDir,
CopyLoopControl* loopControl, BPoint* loc, bool makeOriginalName)
{
try {
CopyFile(srcFile, srcStat, destDir, loopControl, loc,
makeOriginalName);
} catch (status_t error) {
return error;
}
return B_OK;
}
#endif
static status_t
MoveEntryToTrash(BEntry* entry, BPoint* loc, Undo &undo)
{
BDirectory trashDir;
entry_ref ref;
status_t result = entry->GetRef(&ref);
if (result != B_OK)
return result;
node_ref nodeRef;
result = entry->GetNodeRef(&nodeRef);
if (result != B_OK)
return result;
StatStruct statbuf;
result = entry->GetStat(&statbuf);
if (entry->GetStat(&statbuf) != B_OK)
return result;
if (S_ISDIR(statbuf.st_mode)) {
BDirectory dir(entry);
if (dir.IsRootDirectory()) {
BVolume volume(nodeRef.device);
BVolume boot;
BVolumeRoster().GetBootVolume(&boot);
if (volume == boot) {
char name[B_FILE_NAME_LENGTH];
volume.GetName(name);
BString buffer(
B_TRANSLATE("Cannot unmount the boot volume \"%name\"."));
buffer.ReplaceFirst("%name", name);
BAlert* alert = new BAlert("", buffer.String(),
B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL,
B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
} else {
BMessage message(kUnmountVolume);
message.AddInt32("device_id", volume.Device());
be_app->PostMessage(&message);
}
return B_OK;
}
result = FSGetTrashDir(&trashDir, nodeRef.device);
if (result != B_OK)
return result;
BEntry trashEntry;
trashDir.GetEntry(&trashEntry);
if (dir == trashDir || dir.Contains(&trashEntry)) {
BAlert* alert = new BAlert("",
B_TRANSLATE("You cannot put the selected item(s) "
"into the trash."),
B_TRANSLATE("OK"),
0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return B_OK;
}
BMessage message(kCloseWindowAndChildren);
node_ref parentNode;
parentNode.device = statbuf.st_dev;
parentNode.node = statbuf.st_ino;
message.AddData("node_ref", B_RAW_TYPE, &parentNode, sizeof(node_ref));
be_app->PostMessage(&message);
} else {
result = FSGetTrashDir(&trashDir, nodeRef.device);
if (result != B_OK)
return result;
}
char name[B_FILE_NAME_LENGTH];
strlcpy(name, ref.name, sizeof(name));
if (trashDir.Contains(name)) {
BString suffix(" ");
suffix << B_TRANSLATE_COMMENT("copy", "filename copy"),
FSMakeOriginalName(name, &trashDir, suffix.String());
undo.UpdateEntry(entry, name);
}
BNode* sourceNode = 0;
if (loc && loc != (BPoint*)-1 && (sourceNode = GetWritableNode(entry, &statbuf)) != 0) {
trashDir.GetStat(&statbuf);
FSSetPoseLocation(statbuf.st_ino, sourceNode, *loc);
delete sourceNode;
}
BNode node(entry);
BPath path;
if (node.InitCheck() == B_OK && entry->GetPath(&path) == B_OK) {
BString originalPath(path.Path());
node.WriteAttrString(kAttrOriginalPath, &originalPath);
}
TrackerCopyLoopControl loopControl;
MoveItem(entry, &trashDir, loc, kMoveSelectionTo, name, undo, &loopControl);
return B_OK;
}
ConflictCheckResult
PreFlightNameCheck(BObjectList<entry_ref, true>* srcList, const BDirectory* destDir,
int32* collisionCount, uint32 moveMode)
{
*collisionCount = 0;
int32 count = srcList->CountItems();
for (int32 i = 0; i < count; i++) {
entry_ref* srcRef = srcList->ItemAt(i);
BEntry entry(srcRef);
BDirectory parent;
entry.GetParent(&parent);
if (parent != *destDir && destDir->Contains(srcRef->name))
(*collisionCount)++;
}
if (*collisionCount > 1) {
const char* verb = (moveMode == kMoveSelectionTo)
? B_TRANSLATE("moving") : B_TRANSLATE("copying");
BString replaceMsg(B_TRANSLATE_NOCOLLECT(kReplaceManyStr));
replaceMsg.ReplaceAll("%verb", verb);
BAlert* alert = new BAlert();
alert->SetText(replaceMsg.String());
alert->AddButton(B_TRANSLATE("Cancel"));
alert->AddButton(B_TRANSLATE("Prompt"));
alert->AddButton(B_TRANSLATE("Skip all"));
alert->AddButton(B_TRANSLATE("Replace all"));
alert->SetShortcut(0, B_ESCAPE);
switch (alert->Go()) {
case 0:
return kCanceled;
case 1:
return kPrompt;
case 2:
return kSkipAll;
case 3:
return kReplaceAll;
}
}
return kNoConflicts;
}
void
FileStatToString(StatStruct* stat, char* buffer, int32 length)
{
tm timeData;
localtime_r(&stat->st_mtime, &timeData);
BString size;
static BStringFormat format(
B_TRANSLATE("{0, plural, one{# byte} other{# bytes}}"));
format.Format(size, stat->st_size);
uint32 pos = snprintf(buffer, length, "\n\t(%s ", size.String());
strftime(buffer + pos, length - pos, "%b %d %Y, %I:%M:%S %p)", &timeData);
}
status_t
CheckName(uint32 moveMode, const BEntry* sourceEntry,
const BDirectory* destDir, bool multipleCollisions,
ConflictCheckResult& conflictResolution)
{
if (moveMode == kDuplicateSelection) {
return B_OK;
}
const char* name = sourceEntry->Name();
bool sourceIsDirectory = sourceEntry->IsDirectory();
BDirectory srcDirectory;
if (sourceIsDirectory) {
srcDirectory.SetTo(sourceEntry);
BEntry destEntry;
destDir->GetEntry(&destEntry);
if (moveMode != kCreateLink && moveMode != kCreateRelativeLink
&& (srcDirectory == *destDir
|| srcDirectory.Contains(&destEntry))) {
BAlert* alert = new BAlert("",
B_TRANSLATE("You can't move a folder into itself "
"or any of its own sub-folders."), B_TRANSLATE("OK"),
0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return B_ERROR;
}
}
if (FSIsTrashDir(sourceEntry) && moveMode != kCreateLink
&& moveMode != kCreateRelativeLink) {
BAlert* alert = new BAlert("",
B_TRANSLATE("You can't move or copy the trash."),
B_TRANSLATE("OK"), 0, 0, B_WIDTH_AS_USUAL,
B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return B_ERROR;
}
BEntry entry;
if (destDir->FindEntry(name, &entry) != B_OK) {
return B_OK;
}
if (moveMode == kCreateLink || moveMode == kCreateRelativeLink) {
sourceEntry->GetParent(&srcDirectory);
if (srcDirectory == *destDir)
return B_OK;
}
bool destIsDir = entry.IsDirectory();
if (destIsDir) {
BDirectory targetDir(&entry);
if (targetDir.Contains(sourceEntry)) {
BAlert* alert = new BAlert("",
B_TRANSLATE("You can't replace a folder "
"with one of its sub-folders."),
B_TRANSLATE("OK"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return B_ERROR;
}
}
if (moveMode != kCreateLink
&& moveMode != kCreateRelativeLink
&& destIsDir != sourceIsDirectory) {
BAlert* alert = new BAlert("", sourceIsDirectory
? B_TRANSLATE("You cannot replace a file with a folder or a "
"symbolic link.")
: B_TRANSLATE("You cannot replace a folder or a symbolic link "
"with a file."), B_TRANSLATE("OK"), 0, 0, B_WIDTH_AS_USUAL,
B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return B_ERROR;
}
if (conflictResolution == kSkipAll)
return B_ERROR;
if (conflictResolution != kReplaceAll) {
BString replaceMsg;
if (moveMode == kCreateLink || moveMode == kCreateRelativeLink) {
replaceMsg.SetTo(B_TRANSLATE_NOCOLLECT(kSymLinkReplaceStr));
replaceMsg.ReplaceFirst("%name", name);
} else if (sourceEntry->IsDirectory()) {
replaceMsg.SetTo(B_TRANSLATE_NOCOLLECT(kDirectoryReplaceStr));
replaceMsg.ReplaceFirst("%name", name);
replaceMsg.ReplaceFirst("%verb",
moveMode == kMoveSelectionTo
? B_TRANSLATE("moving")
: B_TRANSLATE("copying"));
} else {
char sourceBuffer[96], destBuffer[96];
StatStruct statBuffer;
if (!sourceEntry->IsDirectory()
&& sourceEntry->GetStat(&statBuffer) == B_OK) {
FileStatToString(&statBuffer, sourceBuffer, 96);
} else
sourceBuffer[0] = '\0';
if (!entry.IsDirectory() && entry.GetStat(&statBuffer) == B_OK)
FileStatToString(&statBuffer, destBuffer, 96);
else
destBuffer[0] = '\0';
replaceMsg.SetTo(B_TRANSLATE_NOCOLLECT(kReplaceStr));
replaceMsg.ReplaceAll("%name", name);
replaceMsg.ReplaceFirst("%dest", destBuffer);
replaceMsg.ReplaceFirst("%src", sourceBuffer);
replaceMsg.ReplaceFirst("%movemode", moveMode == kMoveSelectionTo
? B_TRANSLATE("moving") : B_TRANSLATE("copying"));
}
BAlert* alert;
if (multipleCollisions || sourceIsDirectory) {
alert = new BAlert();
alert->SetText(replaceMsg.String());
alert->AddButton(B_TRANSLATE("Skip"));
alert->AddButton(B_TRANSLATE("Skip all"));
alert->AddButton(B_TRANSLATE("Replace"));
alert->AddButton(B_TRANSLATE("Replace all"));
switch (alert->Go()) {
case 0:
conflictResolution = kCanceled;
return B_ERROR;
case 1:
conflictResolution = kSkipAll;
return B_ERROR;
case 2:
conflictResolution = kReplace;
break;
case 3:
conflictResolution = kReplaceAll;
break;
}
} else {
alert = new BAlert("", replaceMsg.String(),
B_TRANSLATE("Cancel"), B_TRANSLATE("Replace"));
alert->SetShortcut(0, B_ESCAPE);
switch (alert->Go()) {
case 0:
conflictResolution = kCanceled;
return B_ERROR;
case 1:
conflictResolution = kReplace;
break;
}
}
}
if (destIsDir)
return B_OK;
status_t status = entry.Remove();
if (status != B_OK) {
BString error(B_TRANSLATE("There was a problem trying to replace "
"\"%name\". The item might be open or busy."));
error.ReplaceFirst("%name", name);
BAlert* alert = new BAlert("", error.String(),
B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
}
return status;
}
status_t
FSDeleteFolder(BEntry* dirEntry, CopyLoopControl* loopControl,
bool updateStatus, bool deleteTopDir, bool upateFileNameInStatus)
{
BDirectory dir(dirEntry);
BEntry entry;
while (dir.GetNextEntry(&entry) == B_OK) {
entry_ref ref;
entry.GetRef(&ref);
if (loopControl->CheckUserCanceled())
return kTrashCanceled;
status_t status;
if (entry.IsDirectory())
status = FSDeleteFolder(&entry, loopControl, updateStatus, true,
upateFileNameInStatus);
else {
status = entry.Remove();
if (updateStatus) {
loopControl->UpdateStatus(upateFileNameInStatus ? ref.name
: "", ref, 1, true);
}
}
if (status == kTrashCanceled)
return kTrashCanceled;
if (status != B_OK) {
loopControl->FileError(B_TRANSLATE_NOCOLLECT(
kFileDeleteErrorString), ref.name, status, false);
}
}
if (loopControl->CheckUserCanceled())
return kTrashCanceled;
entry_ref ref;
dirEntry->GetRef(&ref);
if (updateStatus && deleteTopDir)
loopControl->UpdateStatus(NULL, ref, 1);
if (deleteTopDir)
return dirEntry->Remove();
return B_OK;
}
void
FSMakeOriginalName(BString &string, const BDirectory* destDir,
const char* suffix)
{
if (!destDir->Contains(string.String()))
return;
FSMakeOriginalName(string.LockBuffer(B_FILE_NAME_LENGTH),
const_cast<BDirectory*>(destDir), suffix ? suffix : NULL);
string.UnlockBuffer();
}
void
FSMakeOriginalName(char* name, BDirectory* destDir, const char* suffix)
{
char root[B_FILE_NAME_LENGTH];
char copybase[B_FILE_NAME_LENGTH];
char tempName[B_FILE_NAME_LENGTH + 11];
int32 fnum;
if (!destDir->Contains(name))
return;
BString copySuffix(B_TRANSLATE_COMMENT("copy", "filename copy"));
size_t suffixLength = copySuffix.Length();
if (suffix == NULL)
suffix = copySuffix;
bool copycopy = false;
int32 len = (int32)strlen(name);
char* p = name + len - 1;
while ((p > name) && isdigit(*p))
p--;
while ((p > name) && isspace(*p))
p--;
if (p > name) {
if ((p - suffixLength > name)
&& (strncmp(p - suffixLength, suffix, suffixLength + 1) == 0)) {
*(p + 1) = '\0';
copycopy = true;
strncpy(root, name, (uint32)((p - name) - suffixLength));
root[(p - name) - suffixLength] = '\0';
}
}
if (!copycopy) {
if (strlen(name) > B_FILE_NAME_LENGTH - (suffixLength + 4)) {
name[B_FILE_NAME_LENGTH - (suffixLength + 4)] = '\0';
}
strlcpy(root, name, sizeof(root));
strlcat(name, suffix, B_FILE_NAME_LENGTH);
}
strlcpy(copybase, name, sizeof(copybase));
fnum = 1;
strlcpy(tempName, name, sizeof(tempName));
while (destDir->Contains(tempName)) {
snprintf(tempName, sizeof(tempName), "%s %" B_PRId32, copybase,
++fnum);
if (strlen(tempName) > (B_FILE_NAME_LENGTH - 1)) {
root[strlen(root) - 1] = '\0';
snprintf(tempName, sizeof(tempName), "%s%s %" B_PRId32, root,
suffix, fnum);
}
}
strlcpy(name, tempName, B_FILE_NAME_LENGTH);
}
status_t
FSRecursiveCalcSize(BInfoWindow* window, CopyLoopControl* loopControl,
BDirectory* dir, off_t* _runningSize, int32* _fileCount, int32* _dirCount)
{
dir->Rewind();
BEntry entry;
while (dir->GetNextEntry(&entry) == B_OK) {
if (window && window->StopCalc())
return B_OK;
if (loopControl->CheckUserCanceled())
return kUserCanceled;
StatStruct statbuf;
status_t status = entry.GetStat(&statbuf);
if (status != B_OK)
return status;
(*_runningSize) += statbuf.st_blocks * 512;
if (S_ISDIR(statbuf.st_mode)) {
BDirectory subdir(&entry);
(*_dirCount)++;
status = FSRecursiveCalcSize(window, loopControl, &subdir,
_runningSize, _fileCount, _dirCount);
if (status != B_OK)
return status;
} else
(*_fileCount)++;
}
return B_OK;
}
status_t
CalcItemsAndSize(CopyLoopControl* loopControl,
BObjectList<entry_ref, true>* refList, ssize_t blockSize, int32* totalCount,
off_t* totalSize)
{
int32 fileCount = 0;
int32 dirCount = 0;
if (blockSize < 0) {
blockSize = 2048;
} else if (blockSize < 1024) {
blockSize = 1024;
if (entry_ref* ref = refList->ItemAt(0)) {
BVolume volume(ref->device);
if (volume.InitCheck() == B_OK)
blockSize = volume.BlockSize();
}
}
if (blockSize > 8192)
blockSize = 8192;
int32 num_items = refList->CountItems();
for (int32 i = 0; i < num_items; i++) {
entry_ref* ref = refList->ItemAt(i);
BEntry entry(ref);
StatStruct statbuf;
entry.GetStat(&statbuf);
if (loopControl->CheckUserCanceled())
return kUserCanceled;
if (S_ISDIR(statbuf.st_mode)) {
BDirectory dir(&entry);
dirCount++;
(*totalSize) += blockSize;
status_t result = FSRecursiveCalcSize(NULL, loopControl, &dir,
totalSize, &fileCount, &dirCount);
if (result != B_OK)
return result;
} else {
fileCount++;
(*totalSize) += statbuf.st_size + blockSize;
}
}
*totalCount += (fileCount + dirCount);
return B_OK;
}
status_t
FSGetTrashDir(BDirectory* trashDir, dev_t dev)
{
if (trashDir == NULL)
return B_BAD_VALUE;
BVolume volume(dev);
status_t result = volume.InitCheck();
if (result != B_OK)
return result;
BPath path;
result = find_directory(B_TRASH_DIRECTORY, &path, false, &volume);
if (result != B_OK)
return result;
result = trashDir->SetTo(path.Path());
if (result != B_OK) {
result = create_directory(path.Path(), 0755);
if (result != B_OK)
return result;
result = trashDir->SetTo(path.Path());
if (result != B_OK)
return result;
}
if (result == B_OK) {
PoseInfo poseInfo;
poseInfo.fInvisible = true;
StatStruct statbuf;
poseInfo.fInitedDirectory = trashDir->GetStat(&statbuf) == B_OK ? statbuf.st_ino : -1LL;
trashDir->WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &poseInfo, sizeof(PoseInfo));
}
return B_OK;
}
status_t
FSGetDeskDir(BDirectory* deskDir)
{
if (deskDir == NULL)
return B_BAD_VALUE;
BPath path;
status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true);
if (result != B_OK)
return result;
result = deskDir->SetTo(path.Path());
if (result != B_OK)
return result;
bool gotIcon = true;
attr_info attrInfo;
size_t size;
const void* data;
if (deskDir->GetAttrInfo(kAttrIcon, &attrInfo) == B_ENTRY_NOT_FOUND) {
data = GetTrackerResources()->LoadResource(B_VECTOR_ICON_TYPE, R_DeskIcon, &size);
if (data != NULL && size > 0)
deskDir->WriteAttr(kAttrIcon, B_VECTOR_ICON_TYPE, 0, data, size);
}
if (deskDir->GetAttrInfo(kAttrIcon, &attrInfo) == B_ENTRY_NOT_FOUND) {
if (deskDir->GetAttrInfo(kAttrLargeIcon, &attrInfo) == B_ENTRY_NOT_FOUND) {
data = GetTrackerResources()->LoadResource('ICON', R_DeskIcon, &size);
if (data != NULL && size > 0)
deskDir->WriteAttr(kAttrLargeIcon, 'ICON', 0, data, size);
else
gotIcon = false;
}
if (deskDir->GetAttrInfo(kAttrMiniIcon, &attrInfo) == B_ENTRY_NOT_FOUND) {
data = GetTrackerResources()->LoadResource('MICN', R_DeskIcon, &size);
if (data != NULL && size > 0)
deskDir->WriteAttr(kAttrMiniIcon, 'MICN', 0, data, size);
else
gotIcon = false;
}
}
if (!gotIcon)
TRESPASS();
return B_OK;
}
status_t
FSGetBootDeskDir(BDirectory* deskDir)
{
BVolume bootVolume;
BVolumeRoster().GetBootVolume(&bootVolume);
BPath path;
status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true,
&bootVolume);
if (result != B_OK)
return result;
return deskDir->SetTo(path.Path());
}
static bool
FSIsDirFlavor(const BEntry* entry, directory_which directoryType)
{
StatStruct dir_stat;
StatStruct entry_stat;
BVolume volume;
BPath path;
if (entry->GetStat(&entry_stat) != B_OK)
return false;
if (volume.SetTo(entry_stat.st_dev) != B_OK)
return false;
if (find_directory(directoryType, &path, false, &volume) != B_OK)
return false;
stat(path.Path(), &dir_stat);
return dir_stat.st_ino == entry_stat.st_ino
&& dir_stat.st_dev == entry_stat.st_dev;
}
bool
FSIsPrintersDir(const BEntry* entry)
{
return FSIsDirFlavor(entry, B_USER_PRINTERS_DIRECTORY);
}
bool
FSIsTrashDir(const BEntry* entry)
{
return FSIsDirFlavor(entry, B_TRASH_DIRECTORY);
}
bool
FSIsDeskDir(const BEntry* entry)
{
BPath path;
status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true);
if (result != B_OK)
return false;
BEntry entryToCompare(path.Path());
return entryToCompare == *entry;
}
bool
FSInDeskDir(const entry_ref* ref)
{
BEntry entry(ref);
if (entry.InitCheck() != B_OK)
return false;
BPath path;
if (find_directory(B_DESKTOP_DIRECTORY, &path, true) != B_OK)
return false;
BDirectory desktop(path.Path());
return desktop.Contains(&entry);
}
bool
FSIsHomeDir(const BEntry* entry)
{
return FSIsDirFlavor(entry, B_USER_DIRECTORY);
}
bool
FSIsQueriesDir(const entry_ref* ref)
{
const BEntry entry(ref);
return DirectoryMatches(&entry, "queries", B_USER_DIRECTORY);
}
bool
FSIsRootDir(const BEntry* entry)
{
BPath path;
if (entry->InitCheck() != B_OK || entry->GetPath(&path) != B_OK)
return false;
return strcmp(path.Path(), "/") == 0;
}
bool
FSInRootDir(const entry_ref* ref)
{
BEntry entry(ref);
if (entry.InitCheck() != B_OK)
return false;
BDirectory root("/");
return root.Contains(&entry);
}
bool
DirectoryMatchesOrContains(const BEntry* entry, directory_which which)
{
BPath path;
if (find_directory(which, &path, false, NULL) != B_OK)
return false;
BEntry dirEntry(path.Path());
if (dirEntry.InitCheck() != B_OK)
return false;
if (dirEntry == *entry)
return true;
BDirectory dir(&dirEntry);
return dir.Contains(entry);
}
bool
DirectoryMatchesOrContains(const BEntry* entry, const char* additionalPath,
directory_which which)
{
BPath path;
if (find_directory(which, &path, false, NULL) != B_OK)
return false;
path.Append(additionalPath);
BEntry dirEntry(path.Path());
if (dirEntry.InitCheck() != B_OK)
return false;
if (dirEntry == *entry)
return true;
BDirectory dir(&dirEntry);
return dir.Contains(entry);
}
bool
DirectoryMatches(const BEntry* entry, directory_which which)
{
BPath path;
if (find_directory(which, &path, false, NULL) != B_OK)
return false;
BEntry dirEntry(path.Path());
if (dirEntry.InitCheck() != B_OK)
return false;
return dirEntry == *entry;
}
bool
DirectoryMatches(const BEntry* entry, const char* additionalPath,
directory_which which)
{
BPath path;
if (find_directory(which, &path, false, NULL) != B_OK)
return false;
path.Append(additionalPath);
BEntry dirEntry(path.Path());
if (dirEntry.InitCheck() != B_OK)
return false;
return dirEntry == *entry;
}
extern status_t
FSFindTrackerSettingsDir(BPath* path, bool autoCreate)
{
status_t result = find_directory(B_USER_SETTINGS_DIRECTORY, path,
autoCreate);
if (result != B_OK)
return result;
path->Append("Tracker");
return mkdir(path->Path(), 0777) ? B_OK : errno;
}
bool
FSInTrashDir(const entry_ref* ref)
{
BEntry entry(ref);
if (entry.InitCheck() != B_OK)
return false;
BDirectory trashDir;
if (FSGetTrashDir(&trashDir, ref->device) != B_OK)
return false;
return trashDir.Contains(&entry);
}
void
FSEmptyTrash()
{
if (find_thread("_tracker_empty_trash_") != B_OK) {
resume_thread(spawn_thread(empty_trash, "_tracker_empty_trash_",
B_NORMAL_PRIORITY, NULL));
}
}
status_t
empty_trash(void*)
{
status_t status = B_OK;
TrackerCopyLoopControl loopControl(kTrashState);
BObjectList<entry_ref, true> srcList;
int32 totalCount = 0;
off_t totalSize = 0;
BVolumeRoster volumeRoster;
BVolume volume;
while (volumeRoster.GetNextVolume(&volume) == B_OK) {
if (volume.IsReadOnly() || !volume.IsPersistent())
continue;
BDirectory trashDirectory;
if (FSGetTrashDir(&trashDirectory, volume.Device()) != B_OK)
continue;
BEntry entry;
trashDirectory.GetEntry(&entry);
entry_ref ref;
entry.GetRef(&ref);
srcList.AddItem(&ref);
status = CalcItemsAndSize(&loopControl, &srcList, volume.BlockSize(),
&totalCount, &totalSize);
srcList.RemoveItemAt(0);
srcList.MakeEmpty();
if (status != B_OK)
break;
totalCount--;
}
if (status == B_OK) {
loopControl.Init(totalCount, totalCount);
volumeRoster.Rewind();
while (volumeRoster.GetNextVolume(&volume) == B_OK) {
if (volume.IsReadOnly() || !volume.IsPersistent())
continue;
BDirectory trashDirectory;
if (FSGetTrashDir(&trashDirectory, volume.Device()) != B_OK)
continue;
BEntry entry;
trashDirectory.GetEntry(&entry);
status = FSDeleteFolder(&entry, &loopControl, true, false);
}
}
if (status != B_OK && status != kTrashCanceled && status != kUserCanceled) {
BAlert* alert = new BAlert("", B_TRANSLATE("Error emptying Trash"),
B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
}
return B_OK;
}
status_t
_DeleteTask(BObjectList<entry_ref, true>* list, bool confirm)
{
if (confirm) {
BAlert* alert = new BAlert("",
B_TRANSLATE_NOCOLLECT(kDeleteConfirmationStr),
B_TRANSLATE("Cancel"), B_TRANSLATE("Move to Trash"),
B_TRANSLATE("Delete"),
B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->SetShortcut(0, B_ESCAPE);
alert->SetShortcut(1, 'm');
alert->SetShortcut(2, 'd');
switch (alert->Go()) {
case 0:
delete list;
return B_CANCELED;
case 1:
default:
FSMoveToTrash(list, NULL, false);
return B_OK;
case 2:
break;
}
}
TrackerCopyLoopControl loopControl(kDeleteState);
int32 totalItems = 0;
int64 totalSize = 0;
status_t status = CalcItemsAndSize(&loopControl, list, 0, &totalItems,
&totalSize);
if (status == B_OK) {
loopControl.Init(totalItems, totalItems);
int32 count = list->CountItems();
for (int32 index = 0; index < count; index++) {
entry_ref ref(*list->ItemAt(index));
BEntry entry(&ref);
loopControl.UpdateStatus(ref.name, ref, 1, true);
if (entry.IsDirectory())
status = FSDeleteFolder(&entry, &loopControl, true, true, true);
else
status = entry.Remove();
}
if (status != kTrashCanceled && status != kUserCanceled
&& status != B_OK) {
BAlert* alert = new BAlert("", B_TRANSLATE("Error deleting items"),
B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL,
B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
}
}
delete list;
return B_OK;
}
status_t
FSRecursiveCreateFolder(BPath path)
{
BEntry entry(path.Path());
if (entry.InitCheck() != B_OK) {
BPath parentPath;
status_t err = path.GetParent(&parentPath);
if (err != B_OK)
return err;
err = FSRecursiveCreateFolder(parentPath);
if (err != B_OK)
return err;
}
entry.SetTo(path.Path());
if (entry.Exists())
return B_FILE_EXISTS;
BDirectory parent;
entry.GetParent(&parent);
parent.CreateDirectory(entry.Name(), NULL);
return B_OK;
}
status_t
_RestoreTask(BObjectList<entry_ref, true>* list)
{
TrackerCopyLoopControl loopControl(kRestoreFromTrashState);
int32 totalItems = 0;
int64 totalSize = 0;
status_t err = CalcItemsAndSize(&loopControl, list, 0, &totalItems,
&totalSize);
if (err == B_OK) {
loopControl.Init(totalItems, totalItems);
int32 count = list->CountItems();
for (int32 index = 0; index < count; index++) {
entry_ref ref(*list->ItemAt(index));
BEntry entry(&ref);
BPath originalPath;
loopControl.UpdateStatus(ref.name, ref, 1, true);
if (FSGetOriginalPath(&entry, &originalPath) != B_OK)
continue;
BEntry originalEntry(originalPath.Path());
BPath parentPath;
err = originalPath.GetParent(&parentPath);
if (err != B_OK)
continue;
BEntry parentEntry(parentPath.Path());
if (parentEntry.InitCheck() != B_OK || !parentEntry.Exists()) {
if (FSRecursiveCreateFolder(parentPath) == B_OK) {
originalEntry.SetTo(originalPath.Path());
if (entry.InitCheck() != B_OK)
continue;
}
}
if (!originalEntry.Exists()) {
BDirectory dir(parentPath.Path());
if (dir.InitCheck() == B_OK) {
const char* leafName = originalEntry.Name();
if (entry.MoveTo(&dir, leafName) == B_OK) {
BNode node(&entry);
if (node.InitCheck() == B_OK)
node.RemoveAttr(kAttrOriginalPath);
}
}
}
err = loopControl.CheckUserCanceled();
if (err != B_OK)
break;
}
}
delete list;
return err;
}
void
FSCreateTrashDirs()
{
BVolume volume;
BVolumeRoster roster;
roster.Rewind();
while (roster.GetNextVolume(&volume) == B_OK) {
if (volume.IsReadOnly() || !volume.IsPersistent() || volume.Capacity() == 0)
continue;
BDirectory trashDir;
FSGetTrashDir(&trashDir, volume.Device());
}
}
status_t
FSCreateNewFolder(entry_ref* ref)
{
node_ref node;
node.device = ref->device;
node.node = ref->directory;
BDirectory dir(&node);
status_t result = dir.InitCheck();
if (result != B_OK)
return result;
BString name(ref->name);
FSMakeOriginalName(name, &dir, " -");
ref->set_name(name.String());
BDirectory newDir;
result = dir.CreateDirectory(name.String(), &newDir);
if (result != B_OK)
return result;
BNodeInfo nodeInfo(&newDir);
nodeInfo.SetType(B_DIR_MIMETYPE);
return result;
}
status_t
FSCreateNewFolderIn(const node_ref* dirNode, entry_ref* newRef,
node_ref* newNode)
{
BDirectory dir(dirNode);
status_t result = dir.InitCheck();
if (result == B_OK) {
char name[B_FILE_NAME_LENGTH];
strlcpy(name, B_TRANSLATE("New folder"), sizeof(name));
int fnum = 1;
while (dir.Contains(name)) {
if (++fnum > 9)
snprintf(name, sizeof(name), B_TRANSLATE("New folder%d"), fnum);
else
snprintf(name, sizeof(name), B_TRANSLATE("New folder %d"), fnum);
}
BDirectory newDir;
result = dir.CreateDirectory(name, &newDir);
if (result == B_OK) {
BEntry entry;
newDir.GetEntry(&entry);
entry.GetRef(newRef);
entry.GetNodeRef(newNode);
BNodeInfo nodeInfo(&newDir);
nodeInfo.SetType(B_DIR_MIMETYPE);
NewFolderUndo undo(*newRef);
return B_OK;
}
}
BAlert* alert = new BAlert("",
B_TRANSLATE("Sorry, could not create a new folder."),
B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return result;
}
ReadAttrResult
ReadAttr(const BNode* node, const char* hostAttrName,
const char* foreignAttrName, type_code type, off_t offset, void* buffer,
size_t length, void (*swapFunc)(void*), bool isForeign)
{
if (!isForeign && node->ReadAttr(hostAttrName, type, offset, buffer,
length) == (ssize_t)length) {
return kReadAttrNativeOK;
}
if (node->ReadAttr(foreignAttrName, type, offset, buffer, length)
!= (ssize_t)length) {
return kReadAttrFailed;
}
if (!swapFunc)
return kReadAttrForeignOK;
(swapFunc)(buffer);
return kReadAttrForeignOK;
}
ReadAttrResult
GetAttrInfo(const BNode* node, const char* hostAttrName,
const char* foreignAttrName, type_code* type, size_t* size)
{
attr_info info;
if (node->GetAttrInfo(hostAttrName, &info) == B_OK) {
if (type)
*type = info.type;
if (size)
*size = (size_t)info.size;
return kReadAttrNativeOK;
}
if (node->GetAttrInfo(foreignAttrName, &info) == B_OK) {
if (type)
*type = info.type;
if (size)
*size = (size_t)info.size;
return kReadAttrForeignOK;
}
return kReadAttrFailed;
}
status_t
FSGetParentVirtualDirectoryAware(const BEntry& entry, entry_ref& _ref)
{
node_ref nodeRef;
if (entry.GetNodeRef(&nodeRef) == B_OK) {
if (VirtualDirectoryManager* manager
= VirtualDirectoryManager::Instance()) {
AutoLocker<VirtualDirectoryManager> managerLocker(manager);
if (manager->GetParentDirectoryDefinitionFile(nodeRef, _ref,
nodeRef)) {
return B_OK;
}
}
}
status_t error;
BDirectory parent;
BEntry parentEntry;
if ((error = entry.GetParent(&parent)) != B_OK
|| (error = parent.GetEntry(&parentEntry)) != B_OK
|| (error = parentEntry.GetRef(&_ref)) != B_OK) {
return error;
}
return B_OK;
}
status_t
FSGetParentVirtualDirectoryAware(const BEntry& entry, BEntry& _entry)
{
node_ref nodeRef;
if (entry.GetNodeRef(&nodeRef) == B_OK) {
if (VirtualDirectoryManager* manager
= VirtualDirectoryManager::Instance()) {
AutoLocker<VirtualDirectoryManager> managerLocker(manager);
entry_ref parentRef;
if (manager->GetParentDirectoryDefinitionFile(nodeRef, parentRef,
nodeRef)) {
return _entry.SetTo(&parentRef);
}
}
}
return entry.GetParent(&_entry);
}
status_t
FSGetParentVirtualDirectoryAware(const BEntry& entry, BNode& _node)
{
entry_ref ref;
status_t error = FSGetParentVirtualDirectoryAware(entry, ref);
if (error == B_OK)
error = _node.SetTo(&ref);
return error;
}
static status_t
TrackerOpenWith(const BMessage* refs)
{
BMessage clone(*refs);
ASSERT(dynamic_cast<TTracker*>(be_app) != NULL);
ASSERT(clone.what != 0);
clone.AddInt32("launchUsingSelector", 0);
be_app->PostMessage(&clone);
return B_OK;
}
static void
AsynchLaunchBinder(void (*func)(const entry_ref*, const BMessage*, bool on),
const entry_ref* appRef, const BMessage* refs, bool openWithOK)
{
BMessage* task = new BMessage;
task->AddPointer("function", (void*)func);
task->AddMessage("refs", refs);
task->AddBool("openWithOK", openWithOK);
if (appRef != NULL)
task->AddRef("appRef", appRef);
extern BLooper* gLaunchLooper;
gLaunchLooper->PostMessage(task);
}
static bool
SniffIfGeneric(const entry_ref* ref)
{
BNode node(ref);
char type[B_MIME_TYPE_LENGTH];
BNodeInfo info(&node);
if (info.GetType(type) == B_OK
&& strcasecmp(type, B_FILE_MIME_TYPE) != 0) {
return false;
}
BPath path(ref);
if (path.Path()) {
node.RemoveAttr(kAttrMIMEType);
update_mime_info(path.Path(), 0, 1, 1);
}
return true;
}
static void
SniffIfGeneric(const BMessage* refs)
{
entry_ref ref;
for (int32 index = 0; ; index++) {
if (refs->FindRef("refs", index, &ref) != B_OK)
break;
SniffIfGeneric(&ref);
}
}
static void
_TrackerLaunchAppWithDocuments(const entry_ref* appRef, const BMessage* refs,
bool openWithOK)
{
team_id team;
status_t error = B_ERROR;
BString alertString;
for (int32 mimesetIt = 0; ; mimesetIt++) {
error = be_roster->Launch(appRef, refs, &team);
if (error == B_ALREADY_RUNNING)
error = B_OK;
if (error == B_OK)
break;
if (mimesetIt > 0)
break;
SniffIfGeneric(refs);
}
if (error == B_OK) {
const node_ref* nodeToClose = 0;
ssize_t numBytes;
if (refs != NULL && refs->FindData("nodeRefsToClose", B_RAW_TYPE,
(const void**)&nodeToClose, &numBytes) == B_OK
&& nodeToClose != NULL) {
TTracker* tracker = dynamic_cast<TTracker*>(be_app);
if (tracker != NULL)
tracker->CloseParent(*nodeToClose);
}
} else {
alertString.SetTo(B_TRANSLATE("Could not open \"%name\" (%error). "));
alertString.ReplaceFirst("%name", appRef->name);
alertString.ReplaceFirst("%error", strerror(error));
if (refs != NULL && openWithOK && error != B_SHUTTING_DOWN) {
alertString << B_TRANSLATE_NOCOLLECT(kFindAlternativeStr);
BAlert* alert = new BAlert("", alertString.String(),
B_TRANSLATE("Cancel"), B_TRANSLATE("Find"), 0,
B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetShortcut(0, B_ESCAPE);
if (alert->Go() == 1)
error = TrackerOpenWith(refs);
} else {
BAlert* alert = new BAlert("", alertString.String(),
B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL,
B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
}
}
}
extern "C" char** environ;
static status_t
LoaderErrorDetails(const entry_ref* app, BString &details)
{
BPath path;
BEntry appEntry(app, true);
status_t result = appEntry.GetPath(&path);
if (result != B_OK)
return result;
char* argv[2] = { const_cast<char*>(path.Path()), 0};
port_id errorPort = create_port(1, "Tracker loader error");
int32 envCount = 0;
while (environ[envCount] != NULL)
envCount++;
char** flatArgs = NULL;
size_t flatArgsSize;
result = __flatten_process_args((const char**)argv, 1,
environ, &envCount, argv[0], &flatArgs, &flatArgsSize);
if (result != B_OK)
return result;
result = _kern_load_image(flatArgs, flatArgsSize, 1, envCount,
B_NORMAL_PRIORITY, B_WAIT_TILL_LOADED, errorPort, 0);
if (result == B_OK) {
return B_ERROR;
}
ssize_t bufferSize;
do {
bufferSize = port_buffer_size_etc(errorPort, B_RELATIVE_TIMEOUT, 0);
} while (bufferSize == B_INTERRUPTED);
if (bufferSize <= B_OK) {
delete_port(errorPort);
return bufferSize;
}
uint8* buffer = (uint8*)malloc(bufferSize);
if (buffer == NULL) {
delete_port(errorPort);
return B_NO_MEMORY;
}
bufferSize = read_port_etc(errorPort, NULL, buffer, bufferSize,
B_RELATIVE_TIMEOUT, 0);
delete_port(errorPort);
if (bufferSize < B_OK) {
free(buffer);
return bufferSize;
}
BMessage message;
result = message.Unflatten((const char*)buffer);
free(buffer);
if (result != B_OK)
return result;
int32 errorCode = B_ERROR;
result = message.FindInt32("error", &errorCode);
if (result != B_OK)
return result;
const char* detailName = NULL;
switch (errorCode) {
case B_MISSING_LIBRARY:
detailName = "missing library";
break;
case B_MISSING_SYMBOL:
detailName = "missing symbol";
break;
}
if (detailName == NULL)
return B_ERROR;
const char* detail;
for (int32 i = 0; message.FindString(detailName, i, &detail) == B_OK;
i++) {
if (i > 0)
details += ", ";
details += detail;
}
return B_OK;
}
static void
_TrackerLaunchDocuments(const entry_ref*, const BMessage* refs,
bool openWithOK)
{
if (refs == NULL)
return;
BMessage copyOfRefs(*refs);
entry_ref documentRef;
if (copyOfRefs.FindRef("refs", &documentRef) != B_OK) {
return;
}
status_t error = B_ERROR;
entry_ref app;
BMessage* refsToPass = NULL;
BString alertString;
const char* alternative = 0;
for (int32 mimesetIt = 0; ; mimesetIt++) {
alertString = "";
error = be_roster->FindApp(&documentRef, &app);
if (error != B_OK && mimesetIt == 0) {
SniffIfGeneric(©OfRefs);
continue;
}
if (error != B_OK) {
alertString.SetTo(B_TRANSLATE("Could not find an application to "
"open \"%name\" (%error). "));
alertString.ReplaceFirst("%name", documentRef.name);
alertString.ReplaceFirst("%error", strerror(error));
if (openWithOK)
alternative = B_TRANSLATE_NOCOLLECT(kFindApplicationStr);
break;
} else {
BEntry appEntry(&app, true);
for (int32 index = 0;;) {
entry_ref ref;
if (copyOfRefs.FindRef("refs", index, &ref) != B_OK)
break;
BEntry documentEntry(&ref, true);
if (appEntry == documentEntry) {
PRINT(("stripping %s, app %s \n", ref.name, app.name));
copyOfRefs.RemoveData("refs", index);
} else {
PRINT(("leaving %s, app %s \n", ref.name, app.name));
index++;
}
}
refsToPass = CountRefs(©OfRefs) > 0 ? ©OfRefs: 0;
team_id team;
error = be_roster->Launch(&app, refsToPass, &team);
if (error == B_ALREADY_RUNNING)
error = B_OK;
if (error == B_OK || mimesetIt != 0)
break;
if (error == B_LAUNCH_FAILED_EXECUTABLE) {
BVolume volume(documentRef.device);
if (volume.IsReadOnly()) {
BMimeType type;
error = BMimeType::GuessMimeType(&documentRef, &type);
if (error != B_OK)
break;
error = be_roster->FindApp(type.Type(), &app);
if (error != B_OK)
break;
error = be_roster->Launch(&app, refs, &team);
if (error == B_ALREADY_RUNNING)
error = B_OK;
if (error == B_OK || mimesetIt != 0)
break;
}
}
SniffIfGeneric(©OfRefs);
}
}
if (error != B_OK && alertString.Length() == 0) {
BString loaderErrorString;
bool openedDocuments = true;
if (!refsToPass) {
openWithOK = false;
openedDocuments = false;
}
if (error == B_UNKNOWN_EXECUTABLE && !refsToPass) {
alertString.SetTo(B_TRANSLATE("\"%name\" is an unsupported "
"executable."));
alertString.ReplaceFirst("%name", app.name);
} else if (error == B_LEGACY_EXECUTABLE && !refsToPass) {
alertString.SetTo(B_TRANSLATE("\"%name\" is a legacy executable. "
"Please obtain an updated version or recompile "
"the application."));
alertString.ReplaceFirst("%name", app.name);
} else if (error == B_LAUNCH_FAILED_EXECUTABLE && !refsToPass) {
alertString.SetTo(B_TRANSLATE("Could not open \"%name\". "
"The file is mistakenly marked as executable. "));
alertString.ReplaceFirst("%name", app.name);
if (!openWithOK) {
alertString << B_TRANSLATE("\nShould this be fixed?");
BAlert* alert = new BAlert("", alertString.String(),
B_TRANSLATE("Cancel"), B_TRANSLATE("Proceed"), 0,
B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetShortcut(0, B_ESCAPE);
if (alert->Go() == 1) {
BEntry entry(&documentRef);
mode_t permissions;
error = entry.GetPermissions(&permissions);
if (error == B_OK) {
error = entry.SetPermissions(permissions
& ~(S_IXUSR | S_IXGRP | S_IXOTH));
}
if (error == B_OK) {
_TrackerLaunchDocuments(NULL, refs, false);
return;
} else {
alertString.SetTo(B_TRANSLATE("Could not update "
"permissions of file \"%name\". %error"));
alertString.ReplaceFirst("%name", app.name);
alertString.ReplaceFirst("%error", strerror(error));
}
} else
return;
}
alternative = B_TRANSLATE_NOCOLLECT(kFindApplicationStr);
} else if (error == B_LAUNCH_FAILED_APP_IN_TRASH) {
alertString.SetTo(B_TRANSLATE("Could not open \"%document\" "
"because application \"%app\" is in the Trash. "));
alertString.ReplaceFirst("%document", documentRef.name);
alertString.ReplaceFirst("%app", app.name);
alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr);
} else if (error == B_LAUNCH_FAILED_APP_NOT_FOUND) {
alertString.SetTo(
B_TRANSLATE("Could not open \"%name\" (%error). "));
alertString.ReplaceFirst("%name", documentRef.name);
alertString.ReplaceFirst("%error", strerror(error));
alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr);
} else if (error == B_MISSING_SYMBOL
&& LoaderErrorDetails(&app, loaderErrorString) == B_OK) {
if (openedDocuments) {
alertString.SetTo(B_TRANSLATE("Could not open \"%document\" "
"with application \"%app\" (Missing symbol: %symbol). "
"\n"));
alertString.ReplaceFirst("%document", documentRef.name);
alertString.ReplaceFirst("%app", app.name);
alertString.ReplaceFirst("%symbol",
loaderErrorString.String());
} else {
alertString.SetTo(B_TRANSLATE("Could not open \"%document\" "
"(Missing symbol: %symbol). \n"));
alertString.ReplaceFirst("%document", documentRef.name);
alertString.ReplaceFirst("%symbol",
loaderErrorString.String());
}
alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr);
} else if (error == B_MISSING_LIBRARY
&& LoaderErrorDetails(&app, loaderErrorString) == B_OK) {
if (openedDocuments) {
alertString.SetTo(B_TRANSLATE("Could not open \"%document\" "
"with application \"%app\" (Missing libraries: %library). "
"\n"));
alertString.ReplaceFirst("%document", documentRef.name);
alertString.ReplaceFirst("%app", app.name);
alertString.ReplaceFirst("%library",
loaderErrorString.String());
} else {
alertString.SetTo(B_TRANSLATE("Could not open \"%document\" "
"(Missing libraries: %library). \n"));
alertString.ReplaceFirst("%document", documentRef.name);
alertString.ReplaceFirst("%library",
loaderErrorString.String());
}
alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr);
} else {
alertString.SetTo(B_TRANSLATE("Could not open \"%document\" with "
"application \"%app\" (%error). "));
alertString.ReplaceFirst("%document", documentRef.name);
alertString.ReplaceFirst("%app", app.name);
alertString.ReplaceFirst("%error", strerror(error));
alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr);
}
}
if (error != B_OK) {
if (openWithOK) {
ASSERT(alternative);
alertString << alternative;
BAlert* alert = new BAlert("", alertString.String(),
B_TRANSLATE("Cancel"), B_TRANSLATE("Find"), 0,
B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetShortcut(0, B_ESCAPE);
if (alert->Go() == 1)
error = TrackerOpenWith(refs);
} else {
BAlert* alert = new BAlert("", alertString.String(),
B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL,
B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
}
}
}
status_t
TrackerLaunch(const entry_ref* appRef, const BMessage* refs, bool async,
bool openWithOK)
{
if (!async)
_TrackerLaunchAppWithDocuments(appRef, refs, openWithOK);
else {
AsynchLaunchBinder(&_TrackerLaunchAppWithDocuments, appRef, refs,
openWithOK);
}
return B_OK;
}
status_t
TrackerLaunch(const entry_ref* appRef, bool async)
{
if (!async)
_TrackerLaunchAppWithDocuments(appRef, NULL, false);
else
AsynchLaunchBinder(&_TrackerLaunchAppWithDocuments, appRef, 0, false);
return B_OK;
}
status_t
TrackerLaunch(const BMessage* refs, bool async, bool openWithOK)
{
if (!async)
_TrackerLaunchDocuments(NULL, refs, openWithOK);
else
AsynchLaunchBinder(&_TrackerLaunchDocuments, NULL, refs, openWithOK);
return B_OK;
}
#if !B_BEOS_VERSION_DANO
_IMPEXP_TRACKER
#endif
status_t
FSLaunchItem(const entry_ref* application, const BMessage* refsReceived,
bool async, bool openWithOK)
{
return TrackerLaunch(application, refsReceived, async, openWithOK);
}
#if !B_BEOS_VERSION_DANO
_IMPEXP_TRACKER
#endif
status_t
FSOpenWith(BMessage* listOfRefs)
{
status_t result = B_ERROR;
listOfRefs->what = B_REFS_RECEIVED;
if (dynamic_cast<TTracker*>(be_app) != NULL)
result = TrackerOpenWith(listOfRefs);
else
ASSERT(!"not yet implemented");
return result;
}
void
FSOpenWithDocuments(const entry_ref* executable, BMessage* documents)
{
TrackerLaunch(executable, documents, true);
delete documents;
}
status_t
FSLaunchUsing(const entry_ref* ref, BMessage* listOfRefs)
{
BMessage temp(B_REFS_RECEIVED);
if (listOfRefs == NULL) {
ASSERT(ref != NULL);
temp.AddRef("refs", ref);
listOfRefs = &temp;
}
FSOpenWith(listOfRefs);
return B_OK;
}
status_t
FSLaunchItem(const entry_ref* appRef, BMessage* refs, int32, bool async)
{
if (refs != NULL)
refs->what = B_REFS_RECEIVED;
status_t result = TrackerLaunch(appRef, refs, async, true);
delete refs;
return result;
}
void
FSLaunchItem(const entry_ref* appRef, BMessage* refs, int32 workspace)
{
FSLaunchItem(appRef, refs, workspace, true);
}
status_t
FSGetOriginalPath(BEntry* entry, BPath* result)
{
status_t err;
entry_ref ref;
err = entry->GetRef(&ref);
if (err != B_OK)
return err;
if (!FSInTrashDir(&ref))
return B_ERROR;
BNode node(entry);
BString originalPath;
if (node.ReadAttrString(kAttrOriginalPath, &originalPath) == B_OK) {
err = result->SetTo(originalPath.String());
return err;
}
BEntry parent(*entry);
err = parent.InitCheck();
if (err != B_OK)
return err;
do {
err = parent.GetParent(&parent);
if (err != B_OK)
return err;
if (FSIsTrashDir(&parent))
return B_ENTRY_NOT_FOUND;
err = node.SetTo(&parent);
if (err != B_OK)
return err;
} while (node.ReadAttrString(kAttrOriginalPath, &originalPath) != B_OK);
err = result->SetTo(originalPath.String());
if (err != B_OK)
return err;
BPath path, pathParent;
err = parent.GetPath(&pathParent);
if (err != B_OK)
return err;
err = entry->GetPath(&path);
if (err != B_OK)
return err;
result->Append(path.Path() + strlen(pathParent.Path()) + 1);
return B_OK;
}
directory_which
WellKnowEntryList::Match(const node_ref* node)
{
const WellKnownEntry* result = MatchEntry(node);
if (result != NULL)
return result->which;
return (directory_which)-1;
}
const WellKnowEntryList::WellKnownEntry*
WellKnowEntryList::MatchEntry(const node_ref* node)
{
if (self == NULL)
self = new WellKnowEntryList();
return self->MatchEntryCommon(node);
}
const WellKnowEntryList::WellKnownEntry*
WellKnowEntryList::MatchEntryCommon(const node_ref* node)
{
uint32 count = entries.size();
for (uint32 index = 0; index < count; index++) {
if (*node == entries[index].node)
return &entries[index];
}
return NULL;
}
void
WellKnowEntryList::Quit()
{
delete self;
self = NULL;
}
void
WellKnowEntryList::AddOne(directory_which which, const char* name)
{
BPath path;
if (find_directory(which, &path, true) != B_OK)
return;
BEntry entry(path.Path(), true);
node_ref node;
if (entry.GetNodeRef(&node) != B_OK)
return;
entries.push_back(WellKnownEntry(&node, which, name));
}
void
WellKnowEntryList::AddOne(directory_which which, directory_which base,
const char* extra, const char* name)
{
BPath path;
if (find_directory(base, &path, true) != B_OK)
return;
path.Append(extra);
BEntry entry(path.Path(), true);
node_ref node;
if (entry.GetNodeRef(&node) != B_OK)
return;
entries.push_back(WellKnownEntry(&node, which, name));
}
void
WellKnowEntryList::AddOne(directory_which which, const char* path,
const char* name)
{
BEntry entry(path, true);
node_ref node;
if (entry.GetNodeRef(&node) != B_OK)
return;
entries.push_back(WellKnownEntry(&node, which, name));
}
WellKnowEntryList::WellKnowEntryList()
{
AddOne(B_SYSTEM_DIRECTORY, "system");
AddOne((directory_which)B_BOOT_DISK, "/boot", "boot");
AddOne(B_USER_DIRECTORY, "home");
AddOne(B_BEOS_FONTS_DIRECTORY, "fonts");
AddOne(B_USER_FONTS_DIRECTORY, "fonts");
AddOne(B_BEOS_APPS_DIRECTORY, "apps");
AddOne(B_APPS_DIRECTORY, "apps");
AddOne((directory_which)B_USER_DESKBAR_APPS_DIRECTORY,
B_USER_DESKBAR_DIRECTORY, "Applications", "apps");
AddOne(B_BEOS_PREFERENCES_DIRECTORY, "preferences");
AddOne(B_PREFERENCES_DIRECTORY, "preferences");
AddOne((directory_which)B_USER_DESKBAR_PREFERENCES_DIRECTORY,
B_USER_DESKBAR_DIRECTORY, "Preferences", "preferences");
AddOne((directory_which)B_USER_MAIL_DIRECTORY, B_USER_DIRECTORY, "mail",
"mail");
AddOne((directory_which)B_USER_QUERIES_DIRECTORY, B_USER_DIRECTORY,
"queries", "queries");
AddOne(B_SYSTEM_DEVELOP_DIRECTORY, "develop");
AddOne((directory_which)B_USER_DESKBAR_DEVELOP_DIRECTORY,
B_USER_DESKBAR_DIRECTORY, "Development", "develop");
AddOne(B_USER_CONFIG_DIRECTORY, "config");
AddOne((directory_which)B_USER_PEOPLE_DIRECTORY, B_USER_DIRECTORY,
"people", "people");
AddOne((directory_which)B_USER_DOWNLOADS_DIRECTORY, B_USER_DIRECTORY,
"downloads", "downloads");
}
WellKnowEntryList* WellKnowEntryList::self = NULL;
}