xref: /haiku/src/kits/tracker/FSUtils.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
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		tempName[B_FILE_NAME_LENGTH + 11];
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 		strlcpy(root, name, sizeof(root));
2418 			// save root name
2419 		strlcat(name, suffix, B_FILE_NAME_LENGTH);
2420 	}
2421 
2422 	strlcpy(copybase, name, sizeof(copybase));
2423 
2424 	// if name already exists then add a number
2425 	fnum = 1;
2426 	strlcpy(tempName, name, sizeof(tempName));
2427 	while (destDir->Contains(tempName)) {
2428 		snprintf(tempName, sizeof(tempName), "%s %" B_PRId32, copybase,
2429 			++fnum);
2430 
2431 		if (strlen(tempName) > (B_FILE_NAME_LENGTH - 1)) {
2432 			// The name has grown too long. Maybe we just went from
2433 			// "<filename> copy 9" to "<filename> copy 10" and that extra
2434 			// character was too much. The solution is to further
2435 			// truncate the 'root' name and continue.
2436 			// ??? should we reset fnum or not ???
2437 			root[strlen(root) - 1] = '\0';
2438 			snprintf(tempName, sizeof(tempName), "%s%s %" B_PRId32, root,
2439 				suffix, fnum);
2440 		}
2441 	}
2442 
2443 	strlcpy(name, tempName, B_FILE_NAME_LENGTH);
2444 }
2445 
2446 
2447 status_t
2448 FSRecursiveCalcSize(BInfoWindow* window, CopyLoopControl* loopControl,
2449 	BDirectory* dir, off_t* _runningSize, int32* _fileCount, int32* _dirCount)
2450 {
2451 	dir->Rewind();
2452 	BEntry entry;
2453 	while (dir->GetNextEntry(&entry) == B_OK) {
2454 		// be sure window hasn't closed
2455 		if (window && window->StopCalc())
2456 			return B_OK;
2457 
2458 		if (loopControl->CheckUserCanceled())
2459 			return kUserCanceled;
2460 
2461 		StatStruct statbuf;
2462 		status_t status = entry.GetStat(&statbuf);
2463 		if (status != B_OK)
2464 			return status;
2465 
2466 		(*_runningSize) += statbuf.st_blocks* 512;
2467 
2468 		if (S_ISDIR(statbuf.st_mode)) {
2469 			BDirectory subdir(&entry);
2470 			(*_dirCount)++;
2471 			status = FSRecursiveCalcSize(window, loopControl, &subdir,
2472 				_runningSize, _fileCount, _dirCount);
2473 			if (status != B_OK)
2474 				return status;
2475 		} else
2476 			(*_fileCount)++;
2477 	}
2478 	return B_OK;
2479 }
2480 
2481 
2482 status_t
2483 CalcItemsAndSize(CopyLoopControl* loopControl,
2484 	BObjectList<entry_ref>* refList, ssize_t blockSize, int32* totalCount,
2485 	off_t* totalSize)
2486 {
2487 	int32 fileCount = 0;
2488 	int32 dirCount = 0;
2489 
2490 	// check block size for sanity
2491 	if (blockSize < 0) {
2492 		// This would point at an error to retrieve the block size from
2493 		// the target volume. The code below cannot be used, it is only
2494 		// meant to get the block size when item operations happen on
2495 		// the source volume.
2496 		blockSize = 2048;
2497 	} else if (blockSize < 1024) {
2498 		blockSize = 1024;
2499 		if (entry_ref* ref = refList->ItemAt(0)) {
2500 			// TODO: This assumes all entries in the list share the same
2501 			// volume...
2502 			BVolume volume(ref->device);
2503 			if (volume.InitCheck() == B_OK)
2504 				blockSize = volume.BlockSize();
2505 		}
2506 	}
2507 	// File systems like ReiserFS may advertize a large block size, but
2508 	// stuff is still packed into blocks, so clamp maximum block size.
2509 	if (blockSize > 8192)
2510 		blockSize = 8192;
2511 
2512 	int32 num_items = refList->CountItems();
2513 	for (int32 i = 0; i < num_items; i++) {
2514 		entry_ref* ref = refList->ItemAt(i);
2515 		BEntry entry(ref);
2516 		StatStruct statbuf;
2517 		entry.GetStat(&statbuf);
2518 
2519 		if (loopControl->CheckUserCanceled())
2520 			return kUserCanceled;
2521 
2522 		if (S_ISDIR(statbuf.st_mode)) {
2523 			BDirectory dir(&entry);
2524 			dirCount++;
2525 			(*totalSize) += blockSize;
2526 			status_t result = FSRecursiveCalcSize(NULL, loopControl, &dir,
2527 				totalSize, &fileCount, &dirCount);
2528 			if (result != B_OK)
2529 				return result;
2530 		} else {
2531 			fileCount++;
2532 			(*totalSize) += statbuf.st_size + blockSize;
2533 		}
2534 	}
2535 
2536 	*totalCount += (fileCount + dirCount);
2537 	return B_OK;
2538 }
2539 
2540 
2541 status_t
2542 FSGetTrashDir(BDirectory* trashDir, dev_t dev)
2543 {
2544 	if (trashDir == NULL)
2545 		return B_BAD_VALUE;
2546 
2547 	BVolume volume(dev);
2548 	status_t result = volume.InitCheck();
2549 	if (result != B_OK)
2550 		return result;
2551 
2552 	BPath path;
2553 	result = find_directory(B_TRASH_DIRECTORY, &path, false, &volume);
2554 	if (result != B_OK)
2555 		return result;
2556 
2557 	result = trashDir->SetTo(path.Path());
2558 	if (result != B_OK) {
2559 		// Trash directory does not exist yet, create it.
2560 		result = create_directory(path.Path(), 0755);
2561 		if (result != B_OK)
2562 			return result;
2563 
2564 		result = trashDir->SetTo(path.Path());
2565 		if (result != B_OK)
2566 			return result;
2567 
2568 		// make Trash directory invisible
2569 		StatStruct sbuf;
2570 		trashDir->GetStat(&sbuf);
2571 
2572 		PoseInfo poseInfo;
2573 		poseInfo.fInvisible = true;
2574 		poseInfo.fInitedDirectory = sbuf.st_ino;
2575 		trashDir->WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &poseInfo,
2576 			sizeof(PoseInfo));
2577 	}
2578 
2579 	// set Trash icons (if they haven't already been set)
2580 	attr_info attrInfo;
2581 	size_t size;
2582 	const void* data;
2583 	if (trashDir->GetAttrInfo(kAttrLargeIcon, &attrInfo) == B_ENTRY_NOT_FOUND) {
2584 		data = GetTrackerResources()->LoadResource('ICON', R_TrashIcon, &size);
2585 		if (data != NULL)
2586 			trashDir->WriteAttr(kAttrLargeIcon, 'ICON', 0, data, size);
2587 	}
2588 
2589 	if (trashDir->GetAttrInfo(kAttrMiniIcon, &attrInfo) == B_ENTRY_NOT_FOUND) {
2590 		data = GetTrackerResources()->LoadResource('MICN', R_TrashIcon, &size);
2591 		if (data != NULL)
2592 			trashDir->WriteAttr(kAttrMiniIcon, 'MICN', 0, data, size);
2593 	}
2594 
2595 	if (trashDir->GetAttrInfo(kAttrIcon, &attrInfo) == B_ENTRY_NOT_FOUND) {
2596 		data = GetTrackerResources()->LoadResource(B_VECTOR_ICON_TYPE,
2597 			R_TrashIcon, &size);
2598 		if (data != NULL)
2599 			trashDir->WriteAttr(kAttrIcon, B_VECTOR_ICON_TYPE, 0, data, size);
2600 	}
2601 
2602 	return B_OK;
2603 }
2604 
2605 
2606 status_t
2607 FSGetDeskDir(BDirectory* deskDir)
2608 {
2609 	if (deskDir == NULL)
2610 		return B_BAD_VALUE;
2611 
2612 	BPath path;
2613 	status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true);
2614 	if (result != B_OK)
2615 		return result;
2616 
2617 	result = deskDir->SetTo(path.Path());
2618 	if (result != B_OK)
2619 		return result;
2620 
2621 	// set Desktop icons (if they haven't already been set)
2622 	attr_info attrInfo;
2623 	size_t size;
2624 	const void* data;
2625 	if (deskDir->GetAttrInfo(kAttrLargeIcon, &attrInfo) == B_ENTRY_NOT_FOUND) {
2626 		data = GetTrackerResources()->LoadResource('ICON', R_DeskIcon, &size);
2627 		if (data != NULL)
2628 			deskDir->WriteAttr(kAttrLargeIcon, 'ICON', 0, data, size);
2629 	}
2630 
2631 	if (deskDir->GetAttrInfo(kAttrMiniIcon, &attrInfo) == B_ENTRY_NOT_FOUND) {
2632 		data = GetTrackerResources()->LoadResource('MICN', R_DeskIcon, &size);
2633 		if (data != NULL)
2634 			deskDir->WriteAttr(kAttrMiniIcon, 'MICN', 0, data, size);
2635 	}
2636 
2637 	if (deskDir->GetAttrInfo(kAttrIcon, &attrInfo) == B_ENTRY_NOT_FOUND) {
2638 		data = GetTrackerResources()->LoadResource(B_VECTOR_ICON_TYPE,
2639 			R_DeskIcon, &size);
2640 		if (data != NULL)
2641 			deskDir->WriteAttr(kAttrIcon, B_VECTOR_ICON_TYPE, 0, data, size);
2642 	}
2643 
2644 	return B_OK;
2645 }
2646 
2647 
2648 status_t
2649 FSGetBootDeskDir(BDirectory* deskDir)
2650 {
2651 	BVolume bootVolume;
2652 	BVolumeRoster().GetBootVolume(&bootVolume);
2653 	BPath path;
2654 
2655 	status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true,
2656 		&bootVolume);
2657 	if (result != B_OK)
2658 		return result;
2659 
2660 	return deskDir->SetTo(path.Path());
2661 }
2662 
2663 
2664 static bool
2665 FSIsDirFlavor(const BEntry* entry, directory_which directoryType)
2666 {
2667 	StatStruct dir_stat;
2668 	StatStruct entry_stat;
2669 	BVolume volume;
2670 	BPath path;
2671 
2672 	if (entry->GetStat(&entry_stat) != B_OK)
2673 		return false;
2674 
2675 	if (volume.SetTo(entry_stat.st_dev) != B_OK)
2676 		return false;
2677 
2678 	if (find_directory(directoryType, &path, false, &volume) != B_OK)
2679 		return false;
2680 
2681 	stat(path.Path(), &dir_stat);
2682 
2683 	return dir_stat.st_ino == entry_stat.st_ino
2684 		&& dir_stat.st_dev == entry_stat.st_dev;
2685 }
2686 
2687 
2688 bool
2689 FSIsPrintersDir(const BEntry* entry)
2690 {
2691 	return FSIsDirFlavor(entry, B_USER_PRINTERS_DIRECTORY);
2692 }
2693 
2694 
2695 bool
2696 FSIsTrashDir(const BEntry* entry)
2697 {
2698 	return FSIsDirFlavor(entry, B_TRASH_DIRECTORY);
2699 }
2700 
2701 
2702 bool
2703 FSIsDeskDir(const BEntry* entry)
2704 {
2705 	BPath path;
2706 	status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true);
2707 	if (result != B_OK)
2708 		return false;
2709 
2710 	BEntry entryToCompare(path.Path());
2711 	return entryToCompare == *entry;
2712 }
2713 
2714 
2715 bool
2716 FSIsHomeDir(const BEntry* entry)
2717 {
2718 	return FSIsDirFlavor(entry, B_USER_DIRECTORY);
2719 }
2720 
2721 
2722 bool
2723 FSIsRootDir(const BEntry* entry)
2724 {
2725 	BPath path(entry);
2726 	return path == "/";
2727 }
2728 
2729 
2730 bool
2731 DirectoryMatchesOrContains(const BEntry* entry, directory_which which)
2732 {
2733 	BPath path;
2734 	if (find_directory(which, &path, false, NULL) != B_OK)
2735 		return false;
2736 
2737 	BEntry dirEntry(path.Path());
2738 	if (dirEntry.InitCheck() != B_OK)
2739 		return false;
2740 
2741 	if (dirEntry == *entry)
2742 		// root level match
2743 		return true;
2744 
2745 	BDirectory dir(&dirEntry);
2746 	return dir.Contains(entry);
2747 }
2748 
2749 
2750 bool
2751 DirectoryMatchesOrContains(const BEntry* entry, const char* additionalPath,
2752 	directory_which which)
2753 {
2754 	BPath path;
2755 	if (find_directory(which, &path, false, NULL) != B_OK)
2756 		return false;
2757 
2758 	path.Append(additionalPath);
2759 	BEntry dirEntry(path.Path());
2760 	if (dirEntry.InitCheck() != B_OK)
2761 		return false;
2762 
2763 	if (dirEntry == *entry)
2764 		// root level match
2765 		return true;
2766 
2767 	BDirectory dir(&dirEntry);
2768 	return dir.Contains(entry);
2769 }
2770 
2771 
2772 bool
2773 DirectoryMatches(const BEntry* entry, directory_which which)
2774 {
2775 	BPath path;
2776 	if (find_directory(which, &path, false, NULL) != B_OK)
2777 		return false;
2778 
2779 	BEntry dirEntry(path.Path());
2780 	if (dirEntry.InitCheck() != B_OK)
2781 		return false;
2782 
2783 	return dirEntry == *entry;
2784 }
2785 
2786 
2787 bool
2788 DirectoryMatches(const BEntry* entry, const char* additionalPath,
2789 	directory_which which)
2790 {
2791 	BPath path;
2792 	if (find_directory(which, &path, false, NULL) != B_OK)
2793 		return false;
2794 
2795 	path.Append(additionalPath);
2796 	BEntry dirEntry(path.Path());
2797 	if (dirEntry.InitCheck() != B_OK)
2798 		return false;
2799 
2800 	return dirEntry == *entry;
2801 }
2802 
2803 
2804 extern status_t
2805 FSFindTrackerSettingsDir(BPath* path, bool autoCreate)
2806 {
2807 	status_t result = find_directory(B_USER_SETTINGS_DIRECTORY, path,
2808 		autoCreate);
2809 	if (result != B_OK)
2810 		return result;
2811 
2812 	path->Append("Tracker");
2813 
2814 	return mkdir(path->Path(), 0777) ? B_OK : errno;
2815 }
2816 
2817 
2818 bool
2819 FSInTrashDir(const entry_ref* ref)
2820 {
2821 	BEntry entry(ref);
2822 	if (entry.InitCheck() != B_OK)
2823 		return false;
2824 
2825 	BDirectory trashDir;
2826 	if (FSGetTrashDir(&trashDir, ref->device) != B_OK)
2827 		return false;
2828 
2829 	return trashDir.Contains(&entry);
2830 }
2831 
2832 
2833 void
2834 FSEmptyTrash()
2835 {
2836 	if (find_thread("_tracker_empty_trash_") != B_OK) {
2837 		resume_thread(spawn_thread(empty_trash, "_tracker_empty_trash_",
2838 			B_NORMAL_PRIORITY, NULL));
2839 	}
2840 }
2841 
2842 
2843 status_t
2844 empty_trash(void*)
2845 {
2846 	// empty trash on all mounted volumes
2847 	status_t status = B_OK;
2848 
2849 	TrackerCopyLoopControl loopControl(kTrashState);
2850 
2851 	// calculate the sum total of all items on all volumes in trash
2852 	BObjectList<entry_ref> srcList;
2853 	int32 totalCount = 0;
2854 	off_t totalSize = 0;
2855 
2856 	BVolumeRoster volumeRoster;
2857 	BVolume volume;
2858 	while (volumeRoster.GetNextVolume(&volume) == B_OK) {
2859 		if (volume.IsReadOnly() || !volume.IsPersistent())
2860 			continue;
2861 
2862 		BDirectory trashDirectory;
2863 		if (FSGetTrashDir(&trashDirectory, volume.Device()) != B_OK)
2864 			continue;
2865 
2866 		BEntry entry;
2867 		trashDirectory.GetEntry(&entry);
2868 
2869 		entry_ref ref;
2870 		entry.GetRef(&ref);
2871 		srcList.AddItem(&ref);
2872 		status = CalcItemsAndSize(&loopControl, &srcList, volume.BlockSize(),
2873 			&totalCount, &totalSize);
2874 		if (status != B_OK)
2875 			break;
2876 
2877 		srcList.MakeEmpty();
2878 
2879 		// don't count trash directory itself
2880 		totalCount--;
2881 	}
2882 
2883 	if (status == B_OK) {
2884 		loopControl.Init(totalCount, totalCount);
2885 
2886 		volumeRoster.Rewind();
2887 		while (volumeRoster.GetNextVolume(&volume) == B_OK) {
2888 			if (volume.IsReadOnly() || !volume.IsPersistent())
2889 				continue;
2890 
2891 			BDirectory trashDirectory;
2892 			if (FSGetTrashDir(&trashDirectory, volume.Device()) != B_OK)
2893 				continue;
2894 
2895 			BEntry entry;
2896 			trashDirectory.GetEntry(&entry);
2897 			status = FSDeleteFolder(&entry, &loopControl, true, false);
2898 		}
2899 	}
2900 
2901 	if (status != B_OK && status != kTrashCanceled && status != kUserCanceled) {
2902 		BAlert* alert = new BAlert("", B_TRANSLATE("Error emptying Trash"),
2903 			B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
2904 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
2905 			alert->Go();
2906 	}
2907 
2908 	return B_OK;
2909 }
2910 
2911 
2912 status_t
2913 _DeleteTask(BObjectList<entry_ref>* list, bool confirm)
2914 {
2915 	if (confirm) {
2916 		bool dontMoveToTrash = TrackerSettings().DontMoveFilesToTrash();
2917 
2918 		if (!dontMoveToTrash) {
2919 			BAlert* alert = new BAlert("",
2920 				B_TRANSLATE_NOCOLLECT(kDeleteConfirmationStr),
2921 				B_TRANSLATE("Cancel"), B_TRANSLATE("Move to Trash"),
2922 				B_TRANSLATE("Delete"), B_WIDTH_AS_USUAL, B_OFFSET_SPACING,
2923 				B_WARNING_ALERT);
2924 
2925 			alert->SetShortcut(0, B_ESCAPE);
2926 			alert->SetShortcut(1, 'm');
2927 			alert->SetShortcut(2, 'd');
2928 
2929 			switch (alert->Go()) {
2930 				case 0:
2931 					delete list;
2932 					return B_OK;
2933 				case 1:
2934 					FSMoveToTrash(list, NULL, false);
2935 					return B_OK;
2936 			}
2937 		} else {
2938 			BAlert* alert = new BAlert("",
2939 				B_TRANSLATE_NOCOLLECT(kDeleteConfirmationStr),
2940 				B_TRANSLATE("Cancel"), B_TRANSLATE("Delete"), NULL,
2941 				B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_WARNING_ALERT);
2942 
2943 			alert->SetShortcut(0, B_ESCAPE);
2944 			alert->SetShortcut(1, 'd');
2945 
2946 			if (!alert->Go()) {
2947 				delete list;
2948 				return B_OK;
2949 			}
2950 		}
2951 	}
2952 
2953 	TrackerCopyLoopControl loopControl(kDeleteState);
2954 
2955 	// calculate the sum total of all items on all volumes in trash
2956 	int32 totalItems = 0;
2957 	int64 totalSize = 0;
2958 
2959 	status_t status = CalcItemsAndSize(&loopControl, list, 0, &totalItems,
2960 		&totalSize);
2961 	if (status == B_OK) {
2962 		loopControl.Init(totalItems, totalItems);
2963 
2964 		int32 count = list->CountItems();
2965 		for (int32 index = 0; index < count; index++) {
2966 			entry_ref ref(*list->ItemAt(index));
2967 			BEntry entry(&ref);
2968 			loopControl.UpdateStatus(ref.name, ref, 1, true);
2969 			if (entry.IsDirectory())
2970 				status = FSDeleteFolder(&entry, &loopControl, true, true, true);
2971 			else
2972 				status = entry.Remove();
2973 		}
2974 
2975 		if (status != kTrashCanceled && status != kUserCanceled
2976 			&& status != B_OK) {
2977 			BAlert* alert = new BAlert("", B_TRANSLATE("Error deleting items"),
2978 				B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL,
2979 				B_WARNING_ALERT);
2980 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
2981 			alert->Go();
2982 		}
2983 	}
2984 
2985 	delete list;
2986 
2987 	return B_OK;
2988 }
2989 
2990 status_t
2991 FSRecursiveCreateFolder(BPath path)
2992 {
2993 	BEntry entry(path.Path());
2994 	if (entry.InitCheck() != B_OK) {
2995 		BPath parentPath;
2996 		status_t err = path.GetParent(&parentPath);
2997 		if (err != B_OK)
2998 			return err;
2999 
3000 		err = FSRecursiveCreateFolder(parentPath);
3001 		if (err != B_OK)
3002 			return err;
3003 	}
3004 
3005 	entry.SetTo(path.Path());
3006 	if (entry.Exists())
3007 		return B_FILE_EXISTS;
3008 
3009 	BDirectory parent;
3010 	entry.GetParent(&parent);
3011 	parent.CreateDirectory(entry.Name(), NULL);
3012 
3013 	return B_OK;
3014 }
3015 
3016 status_t
3017 _RestoreTask(BObjectList<entry_ref>* list)
3018 {
3019 	TrackerCopyLoopControl loopControl(kRestoreFromTrashState);
3020 
3021 	// calculate the sum total of all items that will be restored
3022 	int32 totalItems = 0;
3023 	int64 totalSize = 0;
3024 
3025 	status_t err = CalcItemsAndSize(&loopControl, list, 0, &totalItems,
3026 		&totalSize);
3027 	if (err == B_OK) {
3028 		loopControl.Init(totalItems, totalItems);
3029 
3030 		int32 count = list->CountItems();
3031 		for (int32 index = 0; index < count; index++) {
3032 			entry_ref ref(*list->ItemAt(index));
3033 			BEntry entry(&ref);
3034 			BPath originalPath;
3035 
3036 			loopControl.UpdateStatus(ref.name, ref, 1, true);
3037 
3038 			if (FSGetOriginalPath(&entry, &originalPath) != B_OK)
3039 				continue;
3040 
3041 			BEntry originalEntry(originalPath.Path());
3042 			BPath parentPath;
3043 			err = originalPath.GetParent(&parentPath);
3044 			if (err != B_OK)
3045 				continue;
3046 			BEntry parentEntry(parentPath.Path());
3047 
3048 			if (parentEntry.InitCheck() != B_OK || !parentEntry.Exists()) {
3049 				if (FSRecursiveCreateFolder(parentPath) == B_OK) {
3050 					originalEntry.SetTo(originalPath.Path());
3051 					if (entry.InitCheck() != B_OK)
3052 						continue;
3053 				}
3054 			}
3055 
3056 			if (!originalEntry.Exists()) {
3057 				BDirectory dir(parentPath.Path());
3058 				if (dir.InitCheck() == B_OK) {
3059 					const char* leafName = originalEntry.Name();
3060 					if (entry.MoveTo(&dir, leafName) == B_OK) {
3061 						BNode node(&entry);
3062 						if (node.InitCheck() == B_OK)
3063 							node.RemoveAttr(kAttrOriginalPath);
3064 					}
3065 				}
3066 			}
3067 
3068 			err = loopControl.CheckUserCanceled();
3069 			if (err != B_OK)
3070 				break;
3071 		}
3072 	}
3073 
3074 	delete list;
3075 
3076 	return err;
3077 }
3078 
3079 void
3080 FSCreateTrashDirs()
3081 {
3082 	BVolume volume;
3083 	BVolumeRoster roster;
3084 
3085 	roster.Rewind();
3086 	while (roster.GetNextVolume(&volume) == B_OK) {
3087 		if (volume.IsReadOnly() || !volume.IsPersistent())
3088 			continue;
3089 
3090 		BDirectory trashDir;
3091 		FSGetTrashDir(&trashDir, volume.Device());
3092 	}
3093 }
3094 
3095 
3096 status_t
3097 FSCreateNewFolder(const entry_ref* ref)
3098 {
3099 	node_ref node;
3100 	node.device = ref->device;
3101 	node.node = ref->directory;
3102 
3103 	BDirectory dir(&node);
3104 	status_t result = dir.InitCheck();
3105 	if (result != B_OK)
3106 		return result;
3107 
3108 	// ToDo: is that really necessary here?
3109 	BString name(ref->name);
3110 	FSMakeOriginalName(name, &dir, "-");
3111 
3112 	BDirectory newDir;
3113 	result = dir.CreateDirectory(name.String(), &newDir);
3114 	if (result != B_OK)
3115 		return result;
3116 
3117 	BNodeInfo nodeInfo(&newDir);
3118 	nodeInfo.SetType(B_DIR_MIMETYPE);
3119 
3120 	return result;
3121 }
3122 
3123 
3124 status_t
3125 FSCreateNewFolderIn(const node_ref* dirNode, entry_ref* newRef,
3126 	node_ref* newNode)
3127 {
3128 	BDirectory dir(dirNode);
3129 	status_t result = dir.InitCheck();
3130 	if (result == B_OK) {
3131 		char name[B_FILE_NAME_LENGTH];
3132 		strlcpy(name, B_TRANSLATE("New folder"), sizeof(name));
3133 
3134 		int32 fnum = 1;
3135 		while (dir.Contains(name)) {
3136 			// if base name already exists then add a number
3137 			// TODO: move this logic to FSMakeOriginalName
3138 			if (++fnum > 9) {
3139 				snprintf(name, sizeof(name), B_TRANSLATE("New folder%ld"),
3140 					fnum);
3141 			} else {
3142 				snprintf(name, sizeof(name), B_TRANSLATE("New folder %ld"),
3143 					fnum);
3144 			}
3145 		}
3146 
3147 		BDirectory newDir;
3148 		result = dir.CreateDirectory(name, &newDir);
3149 		if (result == B_OK) {
3150 			BEntry entry;
3151 			newDir.GetEntry(&entry);
3152 			entry.GetRef(newRef);
3153 			entry.GetNodeRef(newNode);
3154 
3155 			BNodeInfo nodeInfo(&newDir);
3156 			nodeInfo.SetType(B_DIR_MIMETYPE);
3157 
3158 			// add undo item
3159 			NewFolderUndo undo(*newRef);
3160 			return B_OK;
3161 		}
3162 	}
3163 
3164 	BAlert* alert = new BAlert("",
3165 		B_TRANSLATE("Sorry, could not create a new folder."),
3166 		B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
3167 	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
3168 	alert->Go();
3169 
3170 	return result;
3171 }
3172 
3173 
3174 ReadAttrResult
3175 ReadAttr(const BNode* node, const char* hostAttrName,
3176 	const char* foreignAttrName, type_code type, off_t offset, void* buffer,
3177 	size_t length, void (*swapFunc)(void*), bool isForeign)
3178 {
3179 	if (!isForeign && node->ReadAttr(hostAttrName, type, offset, buffer,
3180 			length) == (ssize_t)length) {
3181 		return kReadAttrNativeOK;
3182 	}
3183 
3184 	// PRINT(("trying %s\n", foreignAttrName));
3185 	// try the other endianness
3186 	if (node->ReadAttr(foreignAttrName, type, offset, buffer, length)
3187 			!= (ssize_t)length) {
3188 		return kReadAttrFailed;
3189 	}
3190 
3191 	// PRINT(("got %s\n", foreignAttrName));
3192 	if (!swapFunc)
3193 		return kReadAttrForeignOK;
3194 
3195 	(swapFunc)(buffer);
3196 		// run the endian swapper
3197 
3198 	return kReadAttrForeignOK;
3199 }
3200 
3201 
3202 ReadAttrResult
3203 GetAttrInfo(const BNode* node, const char* hostAttrName,
3204 	const char* foreignAttrName, type_code* type, size_t* size)
3205 {
3206 	attr_info info;
3207 
3208 	if (node->GetAttrInfo(hostAttrName, &info) == B_OK) {
3209 		if (type)
3210 			*type = info.type;
3211 		if (size)
3212 			*size = (size_t)info.size;
3213 
3214 		return kReadAttrNativeOK;
3215 	}
3216 
3217 	if (node->GetAttrInfo(foreignAttrName, &info) == B_OK) {
3218 		if (type)
3219 			*type = info.type;
3220 		if (size)
3221 			*size = (size_t)info.size;
3222 
3223 		return kReadAttrForeignOK;
3224 	}
3225 	return kReadAttrFailed;
3226 }
3227 
3228 
3229 status_t
3230 FSGetParentVirtualDirectoryAware(const BEntry& entry, entry_ref& _ref)
3231 {
3232 	node_ref nodeRef;
3233 	if (entry.GetNodeRef(&nodeRef) == B_OK) {
3234 		if (VirtualDirectoryManager* manager
3235 				= VirtualDirectoryManager::Instance()) {
3236 			AutoLocker<VirtualDirectoryManager> managerLocker(manager);
3237 			if (manager->GetParentDirectoryDefinitionFile(nodeRef, _ref,
3238 					nodeRef)) {
3239 				return B_OK;
3240 			}
3241 		}
3242 	}
3243 
3244 	status_t error;
3245 	BDirectory parent;
3246 	BEntry parentEntry;
3247 	if ((error = entry.GetParent(&parent)) != B_OK
3248 		|| (error = parent.GetEntry(&parentEntry)) != B_OK
3249 		|| (error = parentEntry.GetRef(&_ref)) != B_OK) {
3250 		return error;
3251 	}
3252 
3253 	return B_OK;
3254 }
3255 
3256 
3257 status_t
3258 FSGetParentVirtualDirectoryAware(const BEntry& entry, BEntry& _entry)
3259 {
3260 	node_ref nodeRef;
3261 	if (entry.GetNodeRef(&nodeRef) == B_OK) {
3262 		if (VirtualDirectoryManager* manager
3263 				= VirtualDirectoryManager::Instance()) {
3264 			AutoLocker<VirtualDirectoryManager> managerLocker(manager);
3265 			entry_ref parentRef;
3266 			if (manager->GetParentDirectoryDefinitionFile(nodeRef, parentRef,
3267 					nodeRef)) {
3268 				return _entry.SetTo(&parentRef);
3269 			}
3270 		}
3271 	}
3272 
3273 	return entry.GetParent(&_entry);
3274 }
3275 
3276 
3277 status_t
3278 FSGetParentVirtualDirectoryAware(const BEntry& entry, BNode& _node)
3279 {
3280 	entry_ref ref;
3281 	status_t error = FSGetParentVirtualDirectoryAware(entry, ref);
3282 	if (error == B_OK)
3283 		error = _node.SetTo(&ref);
3284 
3285 	return error;
3286 }
3287 
3288 
3289 // launching code
3290 
3291 static status_t
3292 TrackerOpenWith(const BMessage* refs)
3293 {
3294 	BMessage clone(*refs);
3295 
3296 	ASSERT(dynamic_cast<TTracker*>(be_app) != NULL);
3297 	ASSERT(clone.what != 0);
3298 
3299 	clone.AddInt32("launchUsingSelector", 0);
3300 	// runs the Open With window
3301 	be_app->PostMessage(&clone);
3302 
3303 	return B_OK;
3304 }
3305 
3306 
3307 static void
3308 AsynchLaunchBinder(void (*func)(const entry_ref*, const BMessage*, bool on),
3309 	const entry_ref* appRef, const BMessage* refs, bool openWithOK)
3310 {
3311 	BMessage* task = new BMessage;
3312 	task->AddPointer("function", (void*)func);
3313 	task->AddMessage("refs", refs);
3314 	task->AddBool("openWithOK", openWithOK);
3315 	if (appRef != NULL)
3316 		task->AddRef("appRef", appRef);
3317 
3318 	extern BLooper* gLaunchLooper;
3319 	gLaunchLooper->PostMessage(task);
3320 }
3321 
3322 
3323 static bool
3324 SniffIfGeneric(const entry_ref* ref)
3325 {
3326 	BNode node(ref);
3327 	char type[B_MIME_TYPE_LENGTH];
3328 	BNodeInfo info(&node);
3329 	if (info.GetType(type) == B_OK
3330 		&& strcasecmp(type, B_FILE_MIME_TYPE) != 0) {
3331 		// already has a type and it's not octet stream
3332 		return false;
3333 	}
3334 
3335 	BPath path(ref);
3336 	if (path.Path()) {
3337 		// force a mimeset
3338 		node.RemoveAttr(kAttrMIMEType);
3339 		update_mime_info(path.Path(), 0, 1, 1);
3340 	}
3341 
3342 	return true;
3343 }
3344 
3345 
3346 static void
3347 SniffIfGeneric(const BMessage* refs)
3348 {
3349 	entry_ref ref;
3350 	for (int32 index = 0; ; index++) {
3351 		if (refs->FindRef("refs", index, &ref) != B_OK)
3352 			break;
3353 		SniffIfGeneric(&ref);
3354 	}
3355 }
3356 
3357 
3358 static void
3359 _TrackerLaunchAppWithDocuments(const entry_ref* appRef, const BMessage* refs,
3360 	bool openWithOK)
3361 {
3362 	team_id team;
3363 
3364 	status_t error = B_ERROR;
3365 	BString alertString;
3366 
3367 	for (int32 mimesetIt = 0; ; mimesetIt++) {
3368 		error = be_roster->Launch(appRef, refs, &team);
3369 		if (error == B_ALREADY_RUNNING)
3370 			// app already running, not really an error
3371 			error = B_OK;
3372 
3373 		if (error == B_OK)
3374 			break;
3375 
3376 		if (mimesetIt > 0)
3377 			break;
3378 
3379 		// failed to open, try mimesetting the refs and launching again
3380 		SniffIfGeneric(refs);
3381 	}
3382 
3383 	if (error == B_OK) {
3384 		// close possible parent window, if specified
3385 		const node_ref* nodeToClose = 0;
3386 		ssize_t numBytes;
3387 		if (refs != NULL && refs->FindData("nodeRefsToClose", B_RAW_TYPE,
3388 				(const void**)&nodeToClose, &numBytes) == B_OK
3389 			&& nodeToClose != NULL) {
3390 			TTracker* tracker = dynamic_cast<TTracker*>(be_app);
3391 			if (tracker != NULL)
3392 				tracker->CloseParent(*nodeToClose);
3393 		}
3394 	} else {
3395 		alertString.SetTo(B_TRANSLATE("Could not open \"%name\" (%error). "));
3396 		alertString.ReplaceFirst("%name", appRef->name);
3397 		alertString.ReplaceFirst("%error", strerror(error));
3398 		if (refs != NULL && openWithOK && error != B_SHUTTING_DOWN) {
3399 			alertString << B_TRANSLATE_NOCOLLECT(kFindAlternativeStr);
3400 			BAlert* alert = new BAlert("", alertString.String(),
3401 				B_TRANSLATE("Cancel"), B_TRANSLATE("Find"), 0,
3402 				B_WIDTH_AS_USUAL, B_WARNING_ALERT);
3403 			alert->SetShortcut(0, B_ESCAPE);
3404 			if (alert->Go() == 1)
3405 				error = TrackerOpenWith(refs);
3406 		} else {
3407 			BAlert* alert = new BAlert("", alertString.String(),
3408 				B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL,
3409 				B_WARNING_ALERT);
3410 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
3411 			alert->Go();
3412 		}
3413 	}
3414 }
3415 
3416 
3417 extern "C" char** environ;
3418 
3419 
3420 static status_t
3421 LoaderErrorDetails(const entry_ref* app, BString &details)
3422 {
3423 	BPath path;
3424 	BEntry appEntry(app, true);
3425 
3426 	status_t result = appEntry.GetPath(&path);
3427 	if (result != B_OK)
3428 		return result;
3429 
3430 	char* argv[2] = { const_cast<char*>(path.Path()), 0};
3431 
3432 	port_id errorPort = create_port(1, "Tracker loader error");
3433 
3434 	// count environment variables
3435 	int32 envCount = 0;
3436 	while (environ[envCount] != NULL)
3437 		envCount++;
3438 
3439 	char** flatArgs = NULL;
3440 	size_t flatArgsSize;
3441 	result = __flatten_process_args((const char**)argv, 1,
3442 		environ, &envCount, argv[0], &flatArgs, &flatArgsSize);
3443 	if (result != B_OK)
3444 		return result;
3445 
3446 	result = _kern_load_image(flatArgs, flatArgsSize, 1, envCount,
3447 		B_NORMAL_PRIORITY, B_WAIT_TILL_LOADED, errorPort, 0);
3448 	if (result == B_OK) {
3449 		// we weren't supposed to be able to start the application...
3450 		return B_ERROR;
3451 	}
3452 
3453 	// read error message from port and construct details string
3454 
3455 	ssize_t bufferSize;
3456 
3457 	do {
3458 		bufferSize = port_buffer_size_etc(errorPort, B_RELATIVE_TIMEOUT, 0);
3459 	} while (bufferSize == B_INTERRUPTED);
3460 
3461 	if (bufferSize <= B_OK) {
3462 		delete_port(errorPort);
3463 		return bufferSize;
3464 	}
3465 
3466 	uint8* buffer = (uint8*)malloc(bufferSize);
3467 	if (buffer == NULL) {
3468 		delete_port(errorPort);
3469 		return B_NO_MEMORY;
3470 	}
3471 
3472 	bufferSize = read_port_etc(errorPort, NULL, buffer, bufferSize,
3473 		B_RELATIVE_TIMEOUT, 0);
3474 	delete_port(errorPort);
3475 
3476 	if (bufferSize < B_OK) {
3477 		free(buffer);
3478 		return bufferSize;
3479 	}
3480 
3481 	BMessage message;
3482 	result = message.Unflatten((const char*)buffer);
3483 	free(buffer);
3484 
3485 	if (result != B_OK)
3486 		return result;
3487 
3488 	int32 errorCode = B_ERROR;
3489 	result = message.FindInt32("error", &errorCode);
3490 	if (result != B_OK)
3491 		return result;
3492 
3493 	const char* detailName = NULL;
3494 	switch (errorCode) {
3495 		case B_MISSING_LIBRARY:
3496 			detailName = "missing library";
3497 			break;
3498 
3499 		case B_MISSING_SYMBOL:
3500 			detailName = "missing symbol";
3501 			break;
3502 	}
3503 
3504 	if (detailName == NULL)
3505 		return B_ERROR;
3506 
3507 	const char* detail;
3508 	for (int32 i = 0; message.FindString(detailName, i, &detail) == B_OK;
3509 			i++) {
3510 		if (i > 0)
3511 			details += ", ";
3512 		details += detail;
3513 	}
3514 
3515 	return B_OK;
3516 }
3517 
3518 
3519 static void
3520 _TrackerLaunchDocuments(const entry_ref*, const BMessage* refs,
3521 	bool openWithOK)
3522 {
3523 	if (refs == NULL)
3524 		return;
3525 
3526 	BMessage copyOfRefs(*refs);
3527 
3528 	entry_ref documentRef;
3529 	if (copyOfRefs.FindRef("refs", &documentRef) != B_OK) {
3530 		// nothing to launch, we are done
3531 		return;
3532 	}
3533 
3534 	status_t error = B_ERROR;
3535 	entry_ref app;
3536 	BMessage* refsToPass = NULL;
3537 	BString alertString;
3538 	const char* alternative = 0;
3539 
3540 	for (int32 mimesetIt = 0; ; mimesetIt++) {
3541 		alertString = "";
3542 		error = be_roster->FindApp(&documentRef, &app);
3543 
3544 		if (error != B_OK && mimesetIt == 0) {
3545 			SniffIfGeneric(&copyOfRefs);
3546 			continue;
3547 		}
3548 
3549 		if (error != B_OK) {
3550 			alertString.SetTo(B_TRANSLATE("Could not find an application to "
3551 				"open \"%name\" (%error). "));
3552 			alertString.ReplaceFirst("%name", documentRef.name);
3553 			alertString.ReplaceFirst("%error", strerror(error));
3554 			if (openWithOK)
3555 				alternative = B_TRANSLATE_NOCOLLECT(kFindApplicationStr);
3556 
3557 			break;
3558 		} else {
3559 			BEntry appEntry(&app, true);
3560 			for (int32 index = 0;;) {
3561 				// remove the app itself from the refs received so we don't
3562 				// try to open ourselves
3563 				entry_ref ref;
3564 				if (copyOfRefs.FindRef("refs", index, &ref) != B_OK)
3565 					break;
3566 
3567 				// deal with symlinks properly
3568 				BEntry documentEntry(&ref, true);
3569 				if (appEntry == documentEntry) {
3570 					PRINT(("stripping %s, app %s \n", ref.name, app.name));
3571 					copyOfRefs.RemoveData("refs", index);
3572 				} else {
3573 					PRINT(("leaving %s, app %s  \n", ref.name, app.name));
3574 					index++;
3575 				}
3576 			}
3577 
3578 			refsToPass = CountRefs(&copyOfRefs) > 0 ? &copyOfRefs: 0;
3579 			team_id team;
3580 			error = be_roster->Launch(&app, refsToPass, &team);
3581 			if (error == B_ALREADY_RUNNING)
3582 				// app already running, not really an error
3583 				error = B_OK;
3584 			if (error == B_OK || mimesetIt != 0)
3585 				break;
3586 
3587 			SniffIfGeneric(&copyOfRefs);
3588 		}
3589 	}
3590 
3591 	if (error != B_OK && alertString.Length() == 0) {
3592 		BString loaderErrorString;
3593 		bool openedDocuments = true;
3594 
3595 		if (!refsToPass) {
3596 			// we just double clicked the app itself, do not offer to
3597 			// find a handling app
3598 			openWithOK = false;
3599 			openedDocuments = false;
3600 		}
3601 		if (error == B_UNKNOWN_EXECUTABLE && !refsToPass) {
3602 			// We know it's an executable, but something unsupported
3603 			alertString.SetTo(B_TRANSLATE("\"%name\" is an unsupported "
3604 				"executable."));
3605 			alertString.ReplaceFirst("%name", app.name);
3606 		} else if (error == B_LEGACY_EXECUTABLE && !refsToPass) {
3607 			// For the moment, this marks an old R3 binary, we may want to
3608 			// extend it to gcc2 binaries someday post R1
3609 			alertString.SetTo(B_TRANSLATE("\"%name\" is a legacy executable. "
3610 				"Please obtain an updated version or recompile "
3611 				"the application."));
3612 			alertString.ReplaceFirst("%name", app.name);
3613 		} else if (error == B_LAUNCH_FAILED_EXECUTABLE && !refsToPass) {
3614 			alertString.SetTo(B_TRANSLATE("Could not open \"%name\". "
3615 				"The file is mistakenly marked as executable. "));
3616 			alertString.ReplaceFirst("%name", app.name);
3617 
3618 			if (!openWithOK) {
3619 				// offer the possibility to change the permissions
3620 
3621 				alertString << B_TRANSLATE("\nShould this be fixed?");
3622 				BAlert* alert = new BAlert("", alertString.String(),
3623 					B_TRANSLATE("Cancel"), B_TRANSLATE("Proceed"), 0,
3624 					B_WIDTH_AS_USUAL, B_WARNING_ALERT);
3625 				alert->SetShortcut(0, B_ESCAPE);
3626 				if (alert->Go() == 1) {
3627 					BEntry entry(&documentRef);
3628 					mode_t permissions;
3629 
3630 					error = entry.GetPermissions(&permissions);
3631 					if (error == B_OK) {
3632 						error = entry.SetPermissions(permissions
3633 							& ~(S_IXUSR | S_IXGRP | S_IXOTH));
3634 					}
3635 					if (error == B_OK) {
3636 						// we updated the permissions, so let's try again
3637 						_TrackerLaunchDocuments(NULL, refs, false);
3638 						return;
3639 					} else {
3640 						alertString.SetTo(B_TRANSLATE("Could not update "
3641 							"permissions of file \"%name\". %error"));
3642 						alertString.ReplaceFirst("%name", app.name);
3643 						alertString.ReplaceFirst("%error", strerror(error));
3644 					}
3645 				} else
3646 					return;
3647 			}
3648 
3649 			alternative = B_TRANSLATE_NOCOLLECT(kFindApplicationStr);
3650 		} else if (error == B_LAUNCH_FAILED_APP_IN_TRASH) {
3651 			alertString.SetTo(B_TRANSLATE("Could not open \"%document\" "
3652 				"because application \"%app\" is in the Trash. "));
3653 			alertString.ReplaceFirst("%document", documentRef.name);
3654 			alertString.ReplaceFirst("%app", app.name);
3655 			alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr);
3656 		} else if (error == B_LAUNCH_FAILED_APP_NOT_FOUND) {
3657 			alertString.SetTo(
3658 				B_TRANSLATE("Could not open \"%name\" (%error). "));
3659 			alertString.ReplaceFirst("%name", documentRef.name);
3660 			alertString.ReplaceFirst("%error", strerror(error));
3661 			alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr);
3662 		} else if (error == B_MISSING_SYMBOL
3663 			&& LoaderErrorDetails(&app, loaderErrorString) == B_OK) {
3664 			if (openedDocuments) {
3665 				alertString.SetTo(B_TRANSLATE("Could not open \"%document\" "
3666 					"with application \"%app\" (Missing symbol: %symbol). "
3667 					"\n"));
3668 				alertString.ReplaceFirst("%document", documentRef.name);
3669 				alertString.ReplaceFirst("%app", app.name);
3670 				alertString.ReplaceFirst("%symbol",
3671 					loaderErrorString.String());
3672 			} else {
3673 				alertString.SetTo(B_TRANSLATE("Could not open \"%document\" "
3674 					"(Missing symbol: %symbol). \n"));
3675 				alertString.ReplaceFirst("%document", documentRef.name);
3676 				alertString.ReplaceFirst("%symbol",
3677 					loaderErrorString.String());
3678 			}
3679 			alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr);
3680 		} else if (error == B_MISSING_LIBRARY
3681 			&& LoaderErrorDetails(&app, loaderErrorString) == B_OK) {
3682 			if (openedDocuments) {
3683 				alertString.SetTo(B_TRANSLATE("Could not open \"%document\" "
3684 					"with application \"%app\" (Missing libraries: %library). "
3685 					"\n"));
3686 				alertString.ReplaceFirst("%document", documentRef.name);
3687 				alertString.ReplaceFirst("%app", app.name);
3688 				alertString.ReplaceFirst("%library",
3689 					loaderErrorString.String());
3690 			} else {
3691 				alertString.SetTo(B_TRANSLATE("Could not open \"%document\" "
3692 					"(Missing libraries: %library). \n"));
3693 				alertString.ReplaceFirst("%document", documentRef.name);
3694 				alertString.ReplaceFirst("%library",
3695 					loaderErrorString.String());
3696 			}
3697 			alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr);
3698 		} else {
3699 			alertString.SetTo(B_TRANSLATE("Could not open \"%document\" with "
3700 				"application \"%app\" (%error). "));
3701 				alertString.ReplaceFirst("%document", documentRef.name);
3702 				alertString.ReplaceFirst("%app", app.name);
3703 				alertString.ReplaceFirst("%error", strerror(error));
3704 			alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr);
3705 		}
3706 	}
3707 
3708 	if (error != B_OK) {
3709 		if (openWithOK) {
3710 			ASSERT(alternative);
3711 			alertString << alternative;
3712 			BAlert* alert = new BAlert("", alertString.String(),
3713 				B_TRANSLATE("Cancel"), B_TRANSLATE("Find"), 0,
3714 				B_WIDTH_AS_USUAL, B_WARNING_ALERT);
3715 			alert->SetShortcut(0, B_ESCAPE);
3716 			if (alert->Go() == 1)
3717 				error = TrackerOpenWith(refs);
3718 		} else {
3719 			BAlert* alert = new BAlert("", alertString.String(),
3720 				B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL,
3721 				B_WARNING_ALERT);
3722 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
3723 			alert->Go();
3724 		}
3725 	}
3726 }
3727 
3728 // the following three calls don't return any reasonable error codes,
3729 // should fix that, making them void
3730 
3731 status_t
3732 TrackerLaunch(const entry_ref* appRef, const BMessage* refs, bool async,
3733 	bool openWithOK)
3734 {
3735 	if (!async)
3736 		_TrackerLaunchAppWithDocuments(appRef, refs, openWithOK);
3737 	else {
3738 		AsynchLaunchBinder(&_TrackerLaunchAppWithDocuments, appRef, refs,
3739 			openWithOK);
3740 	}
3741 
3742 	return B_OK;
3743 }
3744 
3745 status_t
3746 TrackerLaunch(const entry_ref* appRef, bool async)
3747 {
3748 	if (!async)
3749 		_TrackerLaunchAppWithDocuments(appRef, NULL, false);
3750 	else
3751 		AsynchLaunchBinder(&_TrackerLaunchAppWithDocuments, appRef, 0, false);
3752 
3753 	return B_OK;
3754 }
3755 
3756 status_t
3757 TrackerLaunch(const BMessage* refs, bool async, bool openWithOK)
3758 {
3759 	if (!async)
3760 		_TrackerLaunchDocuments(NULL, refs, openWithOK);
3761 	else
3762 		AsynchLaunchBinder(&_TrackerLaunchDocuments, NULL, refs, openWithOK);
3763 
3764 	return B_OK;
3765 }
3766 
3767 
3768 // external launch calls; need to be robust, work if Tracker is not running
3769 
3770 
3771 #if !B_BEOS_VERSION_DANO
3772 _IMPEXP_TRACKER
3773 #endif
3774 status_t
3775 FSLaunchItem(const entry_ref* application, const BMessage* refsReceived,
3776 	bool async, bool openWithOK)
3777 {
3778 	return TrackerLaunch(application, refsReceived, async, openWithOK);
3779 }
3780 
3781 
3782 #if !B_BEOS_VERSION_DANO
3783 _IMPEXP_TRACKER
3784 #endif
3785 status_t
3786 FSOpenWith(BMessage* listOfRefs)
3787 {
3788 	status_t result = B_ERROR;
3789 	listOfRefs->what = B_REFS_RECEIVED;
3790 
3791 	if (dynamic_cast<TTracker*>(be_app) != NULL)
3792 		result = TrackerOpenWith(listOfRefs);
3793 	else
3794 		ASSERT(!"not yet implemented");
3795 
3796 	return result;
3797 }
3798 
3799 
3800 // legacy calls, need for compatibility
3801 
3802 
3803 void
3804 FSOpenWithDocuments(const entry_ref* executable, BMessage* documents)
3805 {
3806 	TrackerLaunch(executable, documents, true);
3807 	delete documents;
3808 }
3809 
3810 
3811 status_t
3812 FSLaunchUsing(const entry_ref* ref, BMessage* listOfRefs)
3813 {
3814 	BMessage temp(B_REFS_RECEIVED);
3815 	if (listOfRefs == NULL) {
3816 		ASSERT(ref != NULL);
3817 		temp.AddRef("refs", ref);
3818 		listOfRefs = &temp;
3819 	}
3820 	FSOpenWith(listOfRefs);
3821 
3822 	return B_OK;
3823 }
3824 
3825 
3826 status_t
3827 FSLaunchItem(const entry_ref* appRef, BMessage* refs, int32, bool async)
3828 {
3829 	if (refs != NULL)
3830 		refs->what = B_REFS_RECEIVED;
3831 
3832 	status_t result = TrackerLaunch(appRef, refs, async, true);
3833 	delete refs;
3834 
3835 	return result;
3836 }
3837 
3838 
3839 void
3840 FSLaunchItem(const entry_ref* appRef, BMessage* refs, int32 workspace)
3841 {
3842 	FSLaunchItem(appRef, refs, workspace, true);
3843 }
3844 
3845 
3846 // Get the original path of an entry in the trash
3847 status_t
3848 FSGetOriginalPath(BEntry* entry, BPath* result)
3849 {
3850 	status_t err;
3851 	entry_ref ref;
3852 	err = entry->GetRef(&ref);
3853 	if (err != B_OK)
3854 		return err;
3855 
3856 	// Only call the routine for entries in the trash
3857 	if (!FSInTrashDir(&ref))
3858 		return B_ERROR;
3859 
3860 	BNode node(entry);
3861 	BString originalPath;
3862 	if (node.ReadAttrString(kAttrOriginalPath, &originalPath) == B_OK) {
3863 		// We're in luck, the entry has the original path in an attribute
3864 		err = result->SetTo(originalPath.String());
3865 		return err;
3866 	}
3867 
3868 	// Iterate the parent directories to find one with
3869 	// the original path attribute
3870 	BEntry parent(*entry);
3871 	err = parent.InitCheck();
3872 	if (err != B_OK)
3873 		return err;
3874 
3875 	// walk up the directory structure until we find a node
3876 	// with original path attribute
3877 	do {
3878 		// move to the parent of this node
3879 		err = parent.GetParent(&parent);
3880 		if (err != B_OK)
3881 			return err;
3882 
3883 		// return if we are at the root of the trash
3884 		if (FSIsTrashDir(&parent))
3885 			return B_ENTRY_NOT_FOUND;
3886 
3887 		// get the parent as a node
3888 		err = node.SetTo(&parent);
3889 		if (err != B_OK)
3890 			return err;
3891 	} while (node.ReadAttrString(kAttrOriginalPath, &originalPath) != B_OK);
3892 
3893 	// Found the attribute, figure out there this file
3894 	// used to live, based on the successfully-read attribute
3895 	err = result->SetTo(originalPath.String());
3896 	if (err != B_OK)
3897 		return err;
3898 
3899 	BPath path, pathParent;
3900 	err = parent.GetPath(&pathParent);
3901 	if (err != B_OK)
3902 		return err;
3903 
3904 	err = entry->GetPath(&path);
3905 	if (err != B_OK)
3906 		return err;
3907 
3908 	result->Append(path.Path() + strlen(pathParent.Path()) + 1);
3909 		// compute the new path by appending the offset of
3910 		// the item we are locating, to the original path
3911 		// of the parent
3912 
3913 	return B_OK;
3914 }
3915 
3916 
3917 directory_which
3918 WellKnowEntryList::Match(const node_ref* node)
3919 {
3920 	const WellKnownEntry* result = MatchEntry(node);
3921 	if (result != NULL)
3922 		return result->which;
3923 
3924 	return (directory_which)-1;
3925 }
3926 
3927 
3928 const WellKnowEntryList::WellKnownEntry*
3929 WellKnowEntryList::MatchEntry(const node_ref* node)
3930 {
3931 	if (self == NULL)
3932 		self = new WellKnowEntryList();
3933 
3934 	return self->MatchEntryCommon(node);
3935 }
3936 
3937 
3938 const WellKnowEntryList::WellKnownEntry*
3939 WellKnowEntryList::MatchEntryCommon(const node_ref* node)
3940 {
3941 	uint32 count = entries.size();
3942 	for (uint32 index = 0; index < count; index++) {
3943 		if (*node == entries[index].node)
3944 			return &entries[index];
3945 	}
3946 
3947 	return NULL;
3948 }
3949 
3950 
3951 void
3952 WellKnowEntryList::Quit()
3953 {
3954 	delete self;
3955 	self = NULL;
3956 }
3957 
3958 
3959 void
3960 WellKnowEntryList::AddOne(directory_which which, const char* name)
3961 {
3962 	BPath path;
3963 	if (find_directory(which, &path, true) != B_OK)
3964 		return;
3965 
3966 	BEntry entry(path.Path(), true);
3967 	node_ref node;
3968 	if (entry.GetNodeRef(&node) != B_OK)
3969 		return;
3970 
3971 	entries.push_back(WellKnownEntry(&node, which, name));
3972 }
3973 
3974 
3975 void
3976 WellKnowEntryList::AddOne(directory_which which, directory_which base,
3977 	const char* extra, const char* name)
3978 {
3979 	BPath path;
3980 	if (find_directory(base, &path, true) != B_OK)
3981 		return;
3982 
3983 	path.Append(extra);
3984 	BEntry entry(path.Path(), true);
3985 	node_ref node;
3986 	if (entry.GetNodeRef(&node) != B_OK)
3987 		return;
3988 
3989 	entries.push_back(WellKnownEntry(&node, which, name));
3990 }
3991 
3992 
3993 void
3994 WellKnowEntryList::AddOne(directory_which which, const char* path,
3995 	const char* name)
3996 {
3997 	BEntry entry(path, true);
3998 	node_ref node;
3999 	if (entry.GetNodeRef(&node) != B_OK)
4000 		return;
4001 
4002 	entries.push_back(WellKnownEntry(&node, which, name));
4003 }
4004 
4005 
4006 WellKnowEntryList::WellKnowEntryList()
4007 {
4008 	AddOne(B_SYSTEM_DIRECTORY, "system");
4009 	AddOne((directory_which)B_BOOT_DISK, "/boot", "boot");
4010 	AddOne(B_USER_DIRECTORY, "home");
4011 
4012 	AddOne(B_BEOS_FONTS_DIRECTORY, "fonts");
4013 	AddOne(B_USER_FONTS_DIRECTORY, "fonts");
4014 
4015 	AddOne(B_BEOS_APPS_DIRECTORY, "apps");
4016 	AddOne(B_APPS_DIRECTORY, "apps");
4017 	AddOne((directory_which)B_USER_DESKBAR_APPS_DIRECTORY,
4018 		B_USER_DESKBAR_DIRECTORY, "Applications", "apps");
4019 
4020 	AddOne(B_BEOS_PREFERENCES_DIRECTORY, "preferences");
4021 	AddOne(B_PREFERENCES_DIRECTORY, "preferences");
4022 	AddOne((directory_which)B_USER_DESKBAR_PREFERENCES_DIRECTORY,
4023 		B_USER_DESKBAR_DIRECTORY, "Preferences", "preferences");
4024 
4025 	AddOne((directory_which)B_USER_MAIL_DIRECTORY, B_USER_DIRECTORY, "mail",
4026 		"mail");
4027 
4028 	AddOne((directory_which)B_USER_QUERIES_DIRECTORY, B_USER_DIRECTORY,
4029 		"queries", "queries");
4030 
4031 	AddOne(B_SYSTEM_DEVELOP_DIRECTORY, "develop");
4032 	AddOne((directory_which)B_USER_DESKBAR_DEVELOP_DIRECTORY,
4033 		B_USER_DESKBAR_DIRECTORY, "Development", "develop");
4034 
4035 	AddOne(B_USER_CONFIG_DIRECTORY, "config");
4036 
4037 	AddOne((directory_which)B_USER_PEOPLE_DIRECTORY, B_USER_DIRECTORY,
4038 		"people", "people");
4039 
4040 	AddOne((directory_which)B_USER_DOWNLOADS_DIRECTORY, B_USER_DIRECTORY,
4041 		"downloads", "downloads");
4042 }
4043 
4044 WellKnowEntryList* WellKnowEntryList::self = NULL;
4045 
4046 } // namespace BPrivate
4047