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