xref: /haiku/src/kits/tracker/FSUtils.cpp (revision 3af8011358bd4c624a0979336d48dabb466171ed)
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 					if (result < 0)
1378 						throw (status_t)result;
1379 					throw (status_t)B_ERROR;
1380 				}
1381 
1382 				result = destFile.Sync();
1383 				if (result != B_OK)
1384 					throw (status_t)result;
1385 
1386 				loopControl->UpdateStatus(NULL, ref, bytes - updateBytes,
1387 					true);
1388 			} else if (bytes < 0) {
1389 				// read error
1390 				throw (status_t)bytes;
1391 			} else {
1392 				// we are done
1393 				break;
1394 			}
1395 		}
1396 
1397 		CopyAttributes(loopControl, &srcFile, &destFile, buffer, bufsize);
1398 	} catch (...) {
1399 		delete[] buffer;
1400 		throw;
1401 	}
1402 
1403 	destFile.SetPermissions(srcStat->st_mode);
1404 	destFile.SetOwner(srcStat->st_uid);
1405 	destFile.SetGroup(srcStat->st_gid);
1406 	destFile.SetModificationTime(srcStat->st_mtime);
1407 	destFile.SetCreationTime(srcStat->st_crtime);
1408 
1409 	delete[] buffer;
1410 
1411 	if (!loopControl->ChecksumFile(&ref)) {
1412 		// File no good.  Remove and quit.
1413 		destFile.Unset();
1414 
1415 		BEntry destEntry;
1416 		if (destDir->FindEntry(destName, &destEntry) == B_OK)
1417 			destEntry.Remove();
1418 		throw (status_t)kUserCanceled;
1419 	}
1420 }
1421 
1422 
1423 void
1424 CopyAttributes(CopyLoopControl* control, BNode* srcNode, BNode* destNode,
1425 	void* buffer, size_t bufsize)
1426 {
1427 	// ToDo:
1428 	// Add error checking
1429 	// prior to coyping attributes, make sure indices are installed
1430 
1431 	// When calling CopyAttributes on files, have to make sure destNode
1432 	// is a BFile opened R/W
1433 
1434 	srcNode->RewindAttrs();
1435 	char name[256];
1436 	while (srcNode->GetNextAttrName(name) == B_OK) {
1437 		// Check to see if this attribute should be skipped.
1438 		if (control->SkipAttribute(name))
1439 			continue;
1440 
1441 		attr_info info;
1442 		if (srcNode->GetAttrInfo(name, &info) != B_OK)
1443 			continue;
1444 
1445 		// Check to see if this attribute should be overwritten when it
1446 		// already exists.
1447 		if (control->PreserveAttribute(name)) {
1448 			attr_info dest_info;
1449 			if (destNode->GetAttrInfo(name, &dest_info) == B_OK)
1450 				continue;
1451 		}
1452 
1453 		// Special case for a size 0 attribute. It wouldn't be written at all
1454 		// otherwise.
1455 		if (info.size == 0)
1456 			destNode->WriteAttr(name, info.type, 0, buffer, 0);
1457 
1458 		ssize_t bytes;
1459 		ssize_t numToRead = (ssize_t)info.size;
1460 		for (off_t offset = 0; numToRead > 0; offset += bytes) {
1461 			size_t chunkSize = (size_t)numToRead;
1462 			if (chunkSize > bufsize)
1463 				chunkSize = bufsize;
1464 
1465 			bytes = srcNode->ReadAttr(name, info.type, offset,
1466 				buffer, chunkSize);
1467 
1468 			if (bytes <= 0)
1469 				break;
1470 
1471 			destNode->WriteAttr(name, info.type, offset, buffer,
1472 				(size_t)bytes);
1473 
1474 			numToRead -= bytes;
1475 		}
1476 	}
1477 }
1478 
1479 
1480 static void
1481 CopyFolder(BEntry* srcEntry, BDirectory* destDir,
1482 	CopyLoopControl* loopControl, BPoint* loc, bool makeOriginalName,
1483 	Undo &undo, bool removeSource = false)
1484 {
1485 	BDirectory newDir;
1486 	BEntry entry;
1487 	status_t err = B_OK;
1488 	bool createDirectory = true;
1489 	BEntry existingEntry;
1490 
1491 	if (loopControl->SkipEntry(srcEntry, false))
1492 		return;
1493 
1494 	entry_ref ref;
1495 	srcEntry->GetRef(&ref);
1496 
1497 	char destName[B_FILE_NAME_LENGTH];
1498 	strlcpy(destName, ref.name, sizeof(destName));
1499 
1500 	loopControl->UpdateStatus(ref.name, ref, 1024, true);
1501 
1502 	if (makeOriginalName) {
1503 		BString suffix(" ");
1504 		suffix << B_TRANSLATE_COMMENT("copy", "filename copy"),
1505 		FSMakeOriginalName(destName, destDir, suffix.String());
1506 		undo.UpdateEntry(srcEntry, destName);
1507 	}
1508 
1509 	if (destDir->FindEntry(destName, &existingEntry) == B_OK) {
1510 		// some entry with a conflicting name is already present in destDir
1511 		// decide what to do about it
1512 		bool isDirectory = existingEntry.IsDirectory();
1513 
1514 		switch (loopControl->OverwriteOnConflict(srcEntry, destName, destDir,
1515 			true, isDirectory)) {
1516 			case TrackerCopyLoopControl::kSkip:
1517 				// we are about to ignore this entire directory
1518 				return;
1519 
1520 
1521 			case TrackerCopyLoopControl::kReplace:
1522 				if (!isDirectory) {
1523 					// conflicting with a file or symbolic link, remove entry
1524 					ThrowOnError(existingEntry.Remove());
1525 					break;
1526 				}
1527 			// fall through if directory, do not replace.
1528 			case TrackerCopyLoopControl::kMerge:
1529 				ASSERT(isDirectory);
1530 				// do not create a new directory, use the current one
1531 				newDir.SetTo(&existingEntry);
1532 				createDirectory = false;
1533 				break;
1534 		}
1535 	}
1536 
1537 	// loop through everything in src folder and copy it to new folder
1538 	BDirectory srcDir(srcEntry);
1539 	srcDir.Rewind();
1540 
1541 	// create a new folder inside of destination folder
1542 	if (createDirectory) {
1543 	 	err = destDir->CreateDirectory(destName, &newDir);
1544 #ifdef _SILENTLY_CORRECT_FILE_NAMES
1545 	 	if (err == B_BAD_VALUE) {
1546 	 		// check if it's an invalid name on a FAT32 file system
1547 	 		if (CreateFileSystemCompatibleName(destDir, destName))
1548 	 			err = destDir->CreateDirectory(destName, &newDir);
1549 	 	}
1550 #endif
1551 		if (err != B_OK) {
1552 			if (!loopControl->FileError(B_TRANSLATE_NOCOLLECT(
1553 					kFolderErrorString), destName, err, true)) {
1554 				throw err;
1555 			}
1556 
1557 			// will allow rest of copy to continue
1558 			return;
1559 		}
1560 	}
1561 
1562 	char* buffer;
1563 	if (createDirectory && err == B_OK
1564 		&& (buffer = (char*)malloc(32768)) != 0) {
1565 		CopyAttributes(loopControl, &srcDir, &newDir, buffer, 32768);
1566 			// don't copy original pose location if new location passed
1567 		free(buffer);
1568 	}
1569 
1570 	StatStruct statbuf;
1571 	srcDir.GetStat(&statbuf);
1572 	dev_t sourceDeviceID = statbuf.st_dev;
1573 
1574 	// copy or write new pose location
1575 	node_ref destNodeRef;
1576 	destDir->GetNodeRef(&destNodeRef);
1577 	SetUpPoseLocation(ref.directory, destNodeRef.node, &srcDir,
1578 		&newDir, loc);
1579 
1580 	while (srcDir.GetNextEntry(&entry) == B_OK) {
1581 
1582 		if (loopControl->CheckUserCanceled())
1583 			throw (status_t)kUserCanceled;
1584 
1585 		entry.GetStat(&statbuf);
1586 
1587 		if (S_ISDIR(statbuf.st_mode)) {
1588 
1589 			// entry is a mount point, do not copy it
1590 			if (statbuf.st_dev != sourceDeviceID) {
1591 				PRINT(("Avoiding mount point %" B_PRIdDEV ", %" B_PRIdDEV "\n",
1592 					statbuf.st_dev, sourceDeviceID));
1593 				continue;
1594 			}
1595 
1596 			CopyFolder(&entry, &newDir, loopControl, 0, false, undo,
1597 				removeSource);
1598 			if (removeSource)
1599 				FSDeleteFolder(&entry, loopControl, true, true, false);
1600 		} else if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) {
1601 			CopyFile(&entry, &statbuf, &newDir, loopControl, 0, false, undo);
1602 			if (removeSource)
1603 				entry.Remove();
1604 		} else {
1605 			// Ignore special files
1606 		}
1607 	}
1608 	if (removeSource)
1609 		srcEntry->Remove();
1610 	else
1611 		srcEntry->Unset();
1612 }
1613 
1614 
1615 status_t
1616 RecursiveMove(BEntry* entry, BDirectory* destDir,
1617 	CopyLoopControl* loopControl)
1618 {
1619 	const char* name = entry->Name();
1620 
1621 	if (destDir->Contains(name)) {
1622 		BPath path (destDir, name);
1623 		BDirectory subDir (path.Path());
1624 		entry_ref ref;
1625 		entry->GetRef(&ref);
1626 		BDirectory source(&ref);
1627 		if (source.InitCheck() == B_OK) {
1628 			source.Rewind();
1629 			BEntry current;
1630 			while (source.GetNextEntry(&current) == B_OK) {
1631 				if (current.IsDirectory()) {
1632 					RecursiveMove(&current, &subDir, loopControl);
1633 					current.Remove();
1634 				} else {
1635 					name = current.Name();
1636 					if (loopControl->OverwriteOnConflict(&current, name,
1637 							&subDir, true, false)
1638 								!= TrackerCopyLoopControl::kSkip) {
1639 						MoveError::FailOnError(current.MoveTo(&subDir,
1640 							NULL, true));
1641 					}
1642 				}
1643 			}
1644 		}
1645 		entry->Remove();
1646 	} else
1647 		MoveError::FailOnError(entry->MoveTo(destDir));
1648 
1649 	return B_OK;
1650 }
1651 
1652 status_t
1653 MoveItem(BEntry* entry, BDirectory* destDir, BPoint* loc, uint32 moveMode,
1654 	const char* newName, Undo &undo, CopyLoopControl* loopControl)
1655 {
1656 	entry_ref ref;
1657 	try {
1658 		node_ref destNode;
1659 		StatStruct statbuf;
1660 		MoveError::FailOnError(entry->GetStat(&statbuf));
1661 		MoveError::FailOnError(entry->GetRef(&ref));
1662 		MoveError::FailOnError(destDir->GetNodeRef(&destNode));
1663 
1664 		if (moveMode == kCreateLink || moveMode == kCreateRelativeLink) {
1665 			PoseInfo poseInfo;
1666 			char name[B_FILE_NAME_LENGTH];
1667 			strlcpy(name, ref.name, sizeof(name));
1668 
1669 			BSymLink link;
1670 			BString suffix(" ");
1671 			suffix << B_TRANSLATE_COMMENT("link", "filename link"),
1672 			FSMakeOriginalName(name, destDir, suffix.String());
1673 			undo.UpdateEntry(entry, name);
1674 
1675 			BPath path;
1676 			entry->GetPath(&path);
1677 			if (loc && loc != (BPoint*)-1) {
1678 				poseInfo.fInvisible = false;
1679 				poseInfo.fInitedDirectory = destNode.node;
1680 				poseInfo.fLocation = *loc;
1681 			}
1682 
1683 			status_t err = B_ERROR;
1684 
1685 			if (moveMode == kCreateRelativeLink) {
1686 				if (statbuf.st_dev == destNode.device) {
1687 					// relative link only works on the same device
1688 					char oldwd[B_PATH_NAME_LENGTH];
1689 					getcwd(oldwd, B_PATH_NAME_LENGTH);
1690 
1691 					BEntry destEntry;
1692 					destDir -> GetEntry(&destEntry);
1693 					BPath destPath;
1694 					destEntry.GetPath(&destPath);
1695 
1696 					chdir(destPath.Path());
1697 						// change working dir to target dir
1698 
1699 					BString destString(destPath.Path());
1700 					destString.Append("/");
1701 
1702 					BString srcString(path.Path());
1703 					srcString.RemoveLast(path.Leaf());
1704 
1705 					// find index while paths are the same
1706 
1707 					const char* src = srcString.String();
1708 					const char* dest = destString.String();
1709 					const char* lastFolderSrc = src;
1710 					const char* lastFolderDest = dest;
1711 
1712 					while (*src && *dest && *src == *dest) {
1713 						++src;
1714 						if (*dest++ == '/') {
1715 							lastFolderSrc = src;
1716 							lastFolderDest = dest;
1717 						}
1718 					}
1719 					src = lastFolderSrc;
1720 					dest = lastFolderDest;
1721 
1722 					BString source;
1723 					if (*dest == '\0' && *src != '\0') {
1724 						// source is deeper in the same tree than the target
1725 						source.Append(src);
1726 					} else if (*dest != '\0') {
1727 						// target is deeper in the same tree than the source
1728 						while (*dest) {
1729 							if (*dest == '/')
1730 								source.Prepend("../");
1731 							++dest;
1732 						}
1733 						source.Append(src);
1734 					}
1735 
1736 					// else source and target are in the same dir
1737 
1738 					source.Append(path.Leaf());
1739 					err = destDir->CreateSymLink(name, source.String(),
1740 						&link);
1741 
1742 					chdir(oldwd);
1743 						// change working dir back to original
1744 				} else
1745 					moveMode = kCreateLink;
1746 						// fall back to absolute link mode
1747 			}
1748 
1749 			if (moveMode == kCreateLink)
1750 				err = destDir->CreateSymLink(name, path.Path(), &link);
1751 
1752 			if (err == B_UNSUPPORTED) {
1753 				throw FailWithAlert(err,
1754 					B_TRANSLATE("The target disk does not support "
1755 					"creating links."), NULL);
1756 			}
1757 
1758 			FailWithAlert::FailOnError(err,
1759 				B_TRANSLATE("Error creating link to \"%name\"."),
1760 				ref.name);
1761 
1762 			if (loc && loc != (BPoint*)-1) {
1763 				link.WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &poseInfo,
1764 					sizeof(PoseInfo));
1765 			}
1766 
1767 			BNodeInfo nodeInfo(&link);
1768 			nodeInfo.SetType(B_LINK_MIMETYPE);
1769 			return B_OK;
1770 		}
1771 
1772 		// if move is on same volume don't copy
1773 		if (statbuf.st_dev == destNode.device && moveMode != kCopySelectionTo
1774 			&& moveMode != kDuplicateSelection) {
1775 
1776 			// for "Move" the size for status is always 1 - since file
1777 			// size is irrelevant when simply moving to a new folder
1778 			loopControl->UpdateStatus(ref.name, ref, 1);
1779 			if (entry->IsDirectory())
1780 				return RecursiveMove(entry, destDir, loopControl);
1781 
1782 			MoveError::FailOnError(entry->MoveTo(destDir, newName));
1783 		} else {
1784 			bool makeOriginalName = (moveMode == kDuplicateSelection);
1785 			if (S_ISDIR(statbuf.st_mode)) {
1786 				CopyFolder(entry, destDir, loopControl, loc, makeOriginalName,
1787 					undo, moveMode == kMoveSelectionTo);
1788 			} else {
1789 				CopyFile(entry, &statbuf, destDir, loopControl, loc,
1790 					makeOriginalName, undo);
1791 				if (moveMode == kMoveSelectionTo)
1792 					entry->Remove();
1793 			}
1794 		}
1795 	} catch (status_t error) {
1796 		// no alert, was already taken care of before
1797 		return error;
1798 	} catch (MoveError& error) {
1799 		BString errorString(B_TRANSLATE("Error moving \"%name\""));
1800 		errorString.ReplaceFirst("%name", ref.name);
1801 		BAlert* alert = new BAlert("", errorString.String(), B_TRANSLATE("OK"),
1802 			0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
1803 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1804 		alert->Go();
1805 		return error.fError;
1806 	} catch (FailWithAlert& error) {
1807 		BString buffer(error.fString);
1808 		if (error.fName != NULL)
1809 			buffer.ReplaceFirst("%name", error.fName);
1810 		else
1811 			buffer << error.fString;
1812 
1813 		BAlert* alert = new BAlert("", buffer.String(), B_TRANSLATE("OK"),
1814 			0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
1815 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1816 		alert->Go();
1817 
1818 		return error.fError;
1819 	}
1820 
1821 	return B_OK;
1822 }
1823 
1824 
1825 void
1826 FSDuplicate(BObjectList<entry_ref>* srcList, BList* pointList)
1827 {
1828 	LaunchInNewThread("DupTask", B_NORMAL_PRIORITY, MoveTask, srcList,
1829 		(BEntry*)NULL, pointList, kDuplicateSelection);
1830 }
1831 
1832 
1833 #if 0
1834 status_t
1835 FSCopyFolder(BEntry* srcEntry, BDirectory* destDir,
1836 	CopyLoopControl* loopControl, BPoint* loc, bool makeOriginalName)
1837 {
1838 	try
1839 		CopyFolder(srcEntry, destDir, loopControl, loc, makeOriginalName);
1840 	catch (status_t error) {
1841 		return error;
1842 
1843 	return B_OK;
1844 }
1845 #endif
1846 
1847 
1848 status_t
1849 FSCopyAttributesAndStats(BNode* srcNode, BNode* destNode, bool copyTimes)
1850 {
1851 	char* buffer = new char[1024];
1852 
1853 	// copy the attributes
1854 	srcNode->RewindAttrs();
1855 	char name[256];
1856 	while (srcNode->GetNextAttrName(name) == B_OK) {
1857 		attr_info info;
1858 		if (srcNode->GetAttrInfo(name, &info) != B_OK)
1859 			continue;
1860 
1861 		attr_info dest_info;
1862 		if (destNode->GetAttrInfo(name, &dest_info) == B_OK)
1863 			continue;
1864 
1865 		ssize_t bytes;
1866 		ssize_t numToRead = (ssize_t)info.size;
1867 		for (off_t offset = 0; numToRead > 0; offset += bytes) {
1868 			size_t chunkSize = (size_t)numToRead;
1869 			if (chunkSize > 1024)
1870 				chunkSize = 1024;
1871 
1872 			bytes = srcNode->ReadAttr(name, info.type, offset, buffer,
1873 				chunkSize);
1874 
1875 			if (bytes <= 0)
1876 				break;
1877 
1878 			destNode->WriteAttr(name, info.type, offset, buffer,
1879 				(size_t)bytes);
1880 
1881 			numToRead -= bytes;
1882 		}
1883 	}
1884 	delete[] buffer;
1885 
1886 	// copy the file stats
1887 	struct stat srcStat;
1888 	srcNode->GetStat(&srcStat);
1889 	destNode->SetPermissions(srcStat.st_mode);
1890 	destNode->SetOwner(srcStat.st_uid);
1891 	destNode->SetGroup(srcStat.st_gid);
1892 	if (copyTimes) {
1893 		destNode->SetModificationTime(srcStat.st_mtime);
1894 		destNode->SetCreationTime(srcStat.st_crtime);
1895 	}
1896 
1897 	return B_OK;
1898 }
1899 
1900 
1901 #if 0
1902 status_t
1903 FSCopyFile(BEntry* srcFile, StatStruct* srcStat, BDirectory* destDir,
1904 	CopyLoopControl* loopControl, BPoint* loc, bool makeOriginalName)
1905 {
1906 	try {
1907 		CopyFile(srcFile, srcStat, destDir, loopControl, loc,
1908 			makeOriginalName);
1909 	} catch (status_t error) {
1910 		return error;
1911 	}
1912 
1913 	return B_OK;
1914 }
1915 #endif
1916 
1917 
1918 static status_t
1919 MoveEntryToTrash(BEntry* entry, BPoint* loc, Undo &undo)
1920 {
1921 	BDirectory trash_dir;
1922 	entry_ref ref;
1923 	status_t result = entry->GetRef(&ref);
1924 	if (result != B_OK)
1925 		return result;
1926 
1927 	node_ref nodeRef;
1928 	result = entry->GetNodeRef(&nodeRef);
1929 	if (result != B_OK)
1930 		return result;
1931 
1932 	StatStruct statbuf;
1933 	result = entry->GetStat(&statbuf);
1934 	if (entry->GetStat(&statbuf) != B_OK)
1935 		return result;
1936 
1937 	// if it's a directory close the window and any child dir windows
1938 	if (S_ISDIR(statbuf.st_mode)) {
1939 		BDirectory dir(entry);
1940 
1941 		// if it's a volume, try to unmount
1942 		if (dir.IsRootDirectory()) {
1943 			BVolume	volume(nodeRef.device);
1944 			BVolume	boot;
1945 
1946 			BVolumeRoster().GetBootVolume(&boot);
1947 			if (volume == boot) {
1948 				char name[B_FILE_NAME_LENGTH];
1949 				volume.GetName(name);
1950 				BString buffer(
1951 					B_TRANSLATE("Cannot unmount the boot volume \"%name\"."));
1952 				buffer.ReplaceFirst("%name", name);
1953 				BAlert* alert = new BAlert("", buffer.String(),
1954 					B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL,
1955 					B_WARNING_ALERT);
1956 				alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1957 				alert->Go();
1958 			} else {
1959 				BMessage message(kUnmountVolume);
1960 				message.AddInt32("device_id", volume.Device());
1961 				be_app->PostMessage(&message);
1962 			}
1963 			return B_OK;
1964 		}
1965 
1966 		// get trash directory on same volume as item being moved
1967 		result = FSGetTrashDir(&trash_dir, nodeRef.device);
1968 		if (result != B_OK)
1969 			return result;
1970 
1971 		// check hierarchy before moving
1972 		BEntry trashEntry;
1973 		trash_dir.GetEntry(&trashEntry);
1974 
1975 		if (dir == trash_dir || dir.Contains(&trashEntry)) {
1976 			BAlert* alert = new BAlert("",
1977 				B_TRANSLATE("You cannot put the selected item(s) "
1978 					"into the trash."),
1979 				B_TRANSLATE("OK"),
1980 				0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
1981 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1982 			alert->Go();
1983 
1984 			// return no error so we don't get two dialogs
1985 			return B_OK;
1986 		}
1987 
1988 		BMessage message(kCloseWindowAndChildren);
1989 
1990 		node_ref parentNode;
1991 		parentNode.device = statbuf.st_dev;
1992 		parentNode.node = statbuf.st_ino;
1993 		message.AddData("node_ref", B_RAW_TYPE, &parentNode, sizeof(node_ref));
1994 		be_app->PostMessage(&message);
1995 	} else {
1996 		// get trash directory on same volume as item being moved
1997 		result = FSGetTrashDir(&trash_dir, nodeRef.device);
1998 		if (result != B_OK)
1999 			return result;
2000 	}
2001 
2002 	// make sure name doesn't conflict with anything in trash already
2003 	char name[B_FILE_NAME_LENGTH];
2004 	strlcpy(name, ref.name, sizeof(name));
2005 	if (trash_dir.Contains(name)) {
2006 		BString suffix(" ");
2007 		suffix << B_TRANSLATE_COMMENT("copy", "filename copy"),
2008 		FSMakeOriginalName(name, &trash_dir, suffix.String());
2009 		undo.UpdateEntry(entry, name);
2010 	}
2011 
2012 	BNode* src_node = 0;
2013 	if (loc && loc != (BPoint*)-1
2014 		&& (src_node = GetWritableNode(entry, &statbuf)) != 0) {
2015 		trash_dir.GetStat(&statbuf);
2016 		PoseInfo poseInfo;
2017 		poseInfo.fInvisible = false;
2018 		poseInfo.fInitedDirectory = statbuf.st_ino;
2019 		poseInfo.fLocation = *loc;
2020 		src_node->WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &poseInfo,
2021 			sizeof(poseInfo));
2022 		delete src_node;
2023 	}
2024 
2025 	BNode node(entry);
2026 	BPath path;
2027 	// Get path of entry before it's moved to the trash
2028 	// and write it to the file as an attribute
2029 	if (node.InitCheck() == B_OK && entry->GetPath(&path) == B_OK) {
2030 		BString originalPath(path.Path());
2031 		node.WriteAttrString(kAttrOriginalPath, &originalPath);
2032 	}
2033 
2034 	TrackerCopyLoopControl loopControl;
2035 	MoveItem(entry, &trash_dir, loc, kMoveSelectionTo, name, undo,
2036 		&loopControl);
2037 	return B_OK;
2038 }
2039 
2040 
2041 ConflictCheckResult
2042 PreFlightNameCheck(BObjectList<entry_ref>* srcList, const BDirectory* destDir,
2043 	int32* collisionCount, uint32 moveMode)
2044 {
2045 	// count the number of name collisions in dest folder
2046 	*collisionCount = 0;
2047 
2048 	int32 count = srcList->CountItems();
2049 	for (int32 i = 0; i < count; i++) {
2050 		entry_ref* srcRef = srcList->ItemAt(i);
2051 		BEntry entry(srcRef);
2052 		BDirectory parent;
2053 		entry.GetParent(&parent);
2054 
2055 		if (parent != *destDir && destDir->Contains(srcRef->name))
2056 			(*collisionCount)++;
2057 	}
2058 
2059 	// prompt user only if there is more than one collision, otherwise the
2060 	// single collision case will be handled as a "Prompt" case by CheckName
2061 	if (*collisionCount > 1) {
2062 		const char* verb = (moveMode == kMoveSelectionTo)
2063 			? B_TRANSLATE("moving")	: B_TRANSLATE("copying");
2064 		BString replaceMsg(B_TRANSLATE_NOCOLLECT(kReplaceManyStr));
2065 		replaceMsg.ReplaceAll("%verb", verb);
2066 
2067 		BAlert* alert = new BAlert();
2068 		alert->SetText(replaceMsg.String());
2069 		alert->AddButton(B_TRANSLATE("Cancel"));
2070 		alert->AddButton(B_TRANSLATE("Prompt"));
2071 		alert->AddButton(B_TRANSLATE("Skip all"));
2072 		alert->AddButton(B_TRANSLATE("Replace all"));
2073 		alert->SetShortcut(0, B_ESCAPE);
2074 		switch (alert->Go()) {
2075 			case 0:
2076 				return kCanceled;
2077 
2078 			case 1:
2079 				// user selected "Prompt"
2080 				return kPrompt;
2081 
2082 			case 2:
2083 				// user selected "Skip all"
2084 				return kSkipAll;
2085 
2086 			case 3:
2087 				// user selected "Replace all"
2088 				return kReplaceAll;
2089 		}
2090 	}
2091 
2092 	return kNoConflicts;
2093 }
2094 
2095 
2096 void
2097 FileStatToString(StatStruct* stat, char* buffer, int32 length)
2098 {
2099 	tm timeData;
2100 	localtime_r(&stat->st_mtime, &timeData);
2101 
2102 	BString size;
2103 	static BStringFormat format(
2104 		B_TRANSLATE("{0, plural, one{# byte} other{# bytes}}"));
2105 	format.Format(size, stat->st_size);
2106 	uint32 pos = snprintf(buffer, length, "\n\t(%s ", size.String());
2107 
2108 	strftime(buffer + pos, length - pos, "%b %d %Y, %I:%M:%S %p)", &timeData);
2109 }
2110 
2111 
2112 status_t
2113 CheckName(uint32 moveMode, const BEntry* sourceEntry,
2114 	const BDirectory* destDir, bool multipleCollisions,
2115 	ConflictCheckResult& conflictResolution)
2116 {
2117 	if (moveMode == kDuplicateSelection) {
2118 		// when duplicating, we will never have a conflict
2119 		return B_OK;
2120 	}
2121 
2122 	// see if item already exists in destination dir
2123 	const char* name = sourceEntry->Name();
2124 	bool sourceIsDirectory = sourceEntry->IsDirectory();
2125 
2126 	BDirectory srcDirectory;
2127 	if (sourceIsDirectory) {
2128 		srcDirectory.SetTo(sourceEntry);
2129 		BEntry destEntry;
2130 		destDir->GetEntry(&destEntry);
2131 
2132 		if (moveMode != kCreateLink && moveMode != kCreateRelativeLink
2133 			&& (srcDirectory == *destDir
2134 				|| srcDirectory.Contains(&destEntry))) {
2135 			BAlert* alert = new BAlert("",
2136 				B_TRANSLATE("You can't move a folder into itself "
2137 				"or any of its own sub-folders."), B_TRANSLATE("OK"),
2138 				0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
2139 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
2140 			alert->Go();
2141 			return B_ERROR;
2142 		}
2143 	}
2144 
2145 	if (FSIsTrashDir(sourceEntry) && moveMode != kCreateLink
2146 		&& moveMode != kCreateRelativeLink) {
2147 		BAlert* alert = new BAlert("",
2148 			B_TRANSLATE("You can't move or copy the trash."),
2149 			B_TRANSLATE("OK"), 0, 0, B_WIDTH_AS_USUAL,
2150 			B_WARNING_ALERT);
2151 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
2152 		alert->Go();
2153 		return B_ERROR;
2154 	}
2155 
2156 	BEntry entry;
2157 	if (destDir->FindEntry(name, &entry) != B_OK) {
2158 		// no conflict, return
2159 		return B_OK;
2160 	}
2161 
2162 	if (moveMode == kCreateLink	|| moveMode == kCreateRelativeLink) {
2163 		// if we are creating link in the same directory, the conflict will
2164 		// be handled later by giving the link a unique name
2165 		sourceEntry->GetParent(&srcDirectory);
2166 
2167 		if (srcDirectory == *destDir)
2168 			return B_OK;
2169 	}
2170 
2171 	bool destIsDir = entry.IsDirectory();
2172 	// be sure not to replace the parent directory of the item being moved
2173 	if (destIsDir) {
2174 		BDirectory targetDir(&entry);
2175 		if (targetDir.Contains(sourceEntry)) {
2176 			BAlert* alert = new BAlert("",
2177 				B_TRANSLATE("You can't replace a folder "
2178 				"with one of its sub-folders."),
2179 				B_TRANSLATE("OK"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
2180 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
2181 			alert->Go();
2182 			return B_ERROR;
2183 		}
2184 	}
2185 
2186 	// ensure that the user isn't trying to replace a file with folder
2187 	// or vice-versa
2188 	if (moveMode != kCreateLink
2189 		&& moveMode != kCreateRelativeLink
2190 		&& destIsDir != sourceIsDirectory) {
2191 		BAlert* alert = new BAlert("", sourceIsDirectory
2192 			? B_TRANSLATE("You cannot replace a file with a folder or a "
2193 				"symbolic link.")
2194 			: B_TRANSLATE("You cannot replace a folder or a symbolic link "
2195 				"with a file."), B_TRANSLATE("OK"),	0, 0, B_WIDTH_AS_USUAL,
2196 			B_WARNING_ALERT);
2197 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
2198 		alert->Go();
2199 		return B_ERROR;
2200 	}
2201 
2202 	if (conflictResolution == kSkipAll)
2203 		return B_ERROR;
2204 
2205 	if (conflictResolution != kReplaceAll) {
2206 		// prompt user to determine whether to replace or not
2207 		BString replaceMsg;
2208 
2209 		if (moveMode == kCreateLink || moveMode == kCreateRelativeLink) {
2210 			replaceMsg.SetTo(B_TRANSLATE_NOCOLLECT(kSymLinkReplaceStr));
2211 			replaceMsg.ReplaceFirst("%name", name);
2212 		} else if (sourceEntry->IsDirectory()) {
2213 			replaceMsg.SetTo(B_TRANSLATE_NOCOLLECT(kDirectoryReplaceStr));
2214 			replaceMsg.ReplaceFirst("%name", name);
2215 			replaceMsg.ReplaceFirst("%verb",
2216 				moveMode == kMoveSelectionTo
2217 				? B_TRANSLATE("moving")
2218 				: B_TRANSLATE("copying"));
2219 		} else {
2220 			char sourceBuffer[96], destBuffer[96];
2221 			StatStruct statBuffer;
2222 
2223 			if (!sourceEntry->IsDirectory()
2224 				&& sourceEntry->GetStat(&statBuffer) == B_OK) {
2225 				FileStatToString(&statBuffer, sourceBuffer, 96);
2226 			} else
2227 				sourceBuffer[0] = '\0';
2228 
2229 			if (!entry.IsDirectory() && entry.GetStat(&statBuffer) == B_OK)
2230 				FileStatToString(&statBuffer, destBuffer, 96);
2231 			else
2232 				destBuffer[0] = '\0';
2233 
2234 			replaceMsg.SetTo(B_TRANSLATE_NOCOLLECT(kReplaceStr));
2235 			replaceMsg.ReplaceAll("%name", name);
2236 			replaceMsg.ReplaceFirst("%dest", destBuffer);
2237 			replaceMsg.ReplaceFirst("%src", sourceBuffer);
2238 			replaceMsg.ReplaceFirst("%movemode", moveMode == kMoveSelectionTo
2239 				? B_TRANSLATE("moving") : B_TRANSLATE("copying"));
2240 		}
2241 
2242 		// special case single collision (don't need Replace All shortcut)
2243 		BAlert* alert;
2244 		if (multipleCollisions || sourceIsDirectory) {
2245 			alert = new BAlert();
2246 			alert->SetText(replaceMsg.String());
2247 			alert->AddButton(B_TRANSLATE("Skip"));
2248 			alert->AddButton(B_TRANSLATE("Skip all"));
2249 			alert->AddButton(B_TRANSLATE("Replace"));
2250 			alert->AddButton(B_TRANSLATE("Replace all"));
2251 			switch (alert->Go()) {
2252 				case 0:
2253 					conflictResolution = kCanceled;
2254 					return B_ERROR;
2255 				case 1:
2256 					conflictResolution = kSkipAll;
2257 					return B_ERROR;
2258 				case 2:
2259 					conflictResolution = kReplace;
2260 					break;
2261 				case 3:
2262 					conflictResolution = kReplaceAll;
2263 					break;
2264 			}
2265 		} else {
2266 			alert = new BAlert("", replaceMsg.String(),
2267 				B_TRANSLATE("Cancel"), B_TRANSLATE("Replace"));
2268 			alert->SetShortcut(0, B_ESCAPE);
2269 			switch (alert->Go()) {
2270 				case 0:
2271 					conflictResolution = kCanceled;
2272 					return B_ERROR;
2273 				case 1:
2274 					conflictResolution = kReplace;
2275 					break;
2276 			}
2277 		}
2278 	}
2279 
2280 	// delete destination item
2281 	if (destIsDir)
2282 		return B_OK;
2283 
2284 	status_t status = entry.Remove();
2285 	if (status != B_OK) {
2286 		BString error(B_TRANSLATE("There was a problem trying to replace "
2287 			"\"%name\". The item might be open or busy."));
2288 		error.ReplaceFirst("%name", name);
2289 		BAlert* alert = new BAlert("", error.String(),
2290 			B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
2291 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
2292 		alert->Go();
2293 	}
2294 
2295 	return status;
2296 }
2297 
2298 
2299 status_t
2300 FSDeleteFolder(BEntry* dirEntry, CopyLoopControl* loopControl,
2301 	bool updateStatus, bool deleteTopDir, bool upateFileNameInStatus)
2302 {
2303 	BDirectory dir(dirEntry);
2304 
2305 	// loop through everything in folder and delete it, skipping trouble files
2306 	BEntry entry;
2307 	while (dir.GetNextEntry(&entry) == B_OK) {
2308 		entry_ref ref;
2309 		entry.GetRef(&ref);
2310 
2311 		if (loopControl->CheckUserCanceled())
2312 			return kTrashCanceled;
2313 
2314 		status_t status;
2315 
2316 		if (entry.IsDirectory())
2317 			status = FSDeleteFolder(&entry, loopControl, updateStatus, true,
2318 				upateFileNameInStatus);
2319 		else {
2320 			status = entry.Remove();
2321 			if (updateStatus) {
2322 				loopControl->UpdateStatus(upateFileNameInStatus ? ref.name
2323 					: "", ref, 1, true);
2324 			}
2325 		}
2326 
2327 		if (status == kTrashCanceled)
2328 			return kTrashCanceled;
2329 
2330 		if (status != B_OK) {
2331 			loopControl->FileError(B_TRANSLATE_NOCOLLECT(
2332 					kFileDeleteErrorString), ref.name, status, false);
2333 		}
2334 	}
2335 
2336 	if (loopControl->CheckUserCanceled())
2337 		return kTrashCanceled;
2338 
2339 	entry_ref ref;
2340 	dirEntry->GetRef(&ref);
2341 
2342 	if (updateStatus && deleteTopDir)
2343 		loopControl->UpdateStatus(NULL, ref, 1);
2344 
2345 	if (deleteTopDir)
2346 		return dirEntry->Remove();
2347 
2348 	return B_OK;
2349 }
2350 
2351 
2352 void
2353 FSMakeOriginalName(BString &string, const BDirectory* destDir,
2354 	const char* suffix)
2355 {
2356 	if (!destDir->Contains(string.String()))
2357 		return;
2358 
2359 	FSMakeOriginalName(string.LockBuffer(B_FILE_NAME_LENGTH),
2360 		const_cast<BDirectory*>(destDir), suffix ? suffix : " copy");
2361 	string.UnlockBuffer();
2362 }
2363 
2364 
2365 void
2366 FSMakeOriginalName(char* name, BDirectory* destDir, const char* suffix)
2367 {
2368 	char		root[B_FILE_NAME_LENGTH];
2369 	char		copybase[B_FILE_NAME_LENGTH];
2370 	char		tempName[B_FILE_NAME_LENGTH + 11];
2371 	int32		fnum;
2372 
2373 	// is this name already original?
2374 	if (!destDir->Contains(name))
2375 		return;
2376 
2377 	// Determine if we're copying a 'copy'. This algorithm isn't perfect.
2378 	// If you're copying a file whose REAL name ends with 'copy' then
2379 	// this method will return "<filename> 1", not "<filename> copy"
2380 
2381 	// However, it will correctly handle file that contain 'copy'
2382 	// elsewhere in their name.
2383 
2384 	bool copycopy = false;		// are we copying a copy?
2385 	int32 len = (int32)strlen(name);
2386 	char* p = name + len - 1;	// get pointer to end os name
2387 
2388 	// eat up optional numbers (if were copying "<filename> copy 34")
2389 	while ((p > name) && isdigit(*p))
2390 		p--;
2391 
2392 	// eat up optional spaces
2393 	while ((p > name) && isspace(*p))
2394 		p--;
2395 
2396 	// now look for the phrase " copy"
2397 	if (p > name) {
2398 		// p points to the last char of the word. For example, 'y' in 'copy'
2399 
2400 		if ((p - 4 > name) && (strncmp(p - 4, suffix, 5) == 0)) {
2401 			// we found 'copy' in the right place.
2402 			// so truncate after 'copy'
2403 			*(p + 1) = '\0';
2404 			copycopy = true;
2405 
2406 			// save the 'root' name of the file, for possible later use.
2407 			// that is copy everything but trailing " copy". Need to
2408 			// NULL terminate after copy
2409 			strncpy(root, name, (uint32)((p - name) - 4));
2410 			root[(p - name) - 4] = '\0';
2411 		}
2412 	}
2413 
2414 	if (!copycopy) {
2415 		// The name can't be longer than B_FILE_NAME_LENGTH.
2416 		// The algoritm adds " copy XX" to the name. That's 8 characters.
2417 		// B_FILE_NAME_LENGTH already accounts for NULL termination so we
2418 		// don't need to save an extra char at the end.
2419 		if (strlen(name) > B_FILE_NAME_LENGTH - 8) {
2420 			// name is too long - truncate it!
2421 			name[B_FILE_NAME_LENGTH - 8] = '\0';
2422 		}
2423 
2424 		strlcpy(root, name, sizeof(root));
2425 			// save root name
2426 		strlcat(name, suffix, B_FILE_NAME_LENGTH);
2427 	}
2428 
2429 	strlcpy(copybase, name, sizeof(copybase));
2430 
2431 	// if name already exists then add a number
2432 	fnum = 1;
2433 	strlcpy(tempName, name, sizeof(tempName));
2434 	while (destDir->Contains(tempName)) {
2435 		snprintf(tempName, sizeof(tempName), "%s %" B_PRId32, copybase,
2436 			++fnum);
2437 
2438 		if (strlen(tempName) > (B_FILE_NAME_LENGTH - 1)) {
2439 			// The name has grown too long. Maybe we just went from
2440 			// "<filename> copy 9" to "<filename> copy 10" and that extra
2441 			// character was too much. The solution is to further
2442 			// truncate the 'root' name and continue.
2443 			// ??? should we reset fnum or not ???
2444 			root[strlen(root) - 1] = '\0';
2445 			snprintf(tempName, sizeof(tempName), "%s%s %" B_PRId32, root,
2446 				suffix, fnum);
2447 		}
2448 	}
2449 
2450 	strlcpy(name, tempName, B_FILE_NAME_LENGTH);
2451 }
2452 
2453 
2454 status_t
2455 FSRecursiveCalcSize(BInfoWindow* window, CopyLoopControl* loopControl,
2456 	BDirectory* dir, off_t* _runningSize, int32* _fileCount, int32* _dirCount)
2457 {
2458 	dir->Rewind();
2459 	BEntry entry;
2460 	while (dir->GetNextEntry(&entry) == B_OK) {
2461 		// be sure window hasn't closed
2462 		if (window && window->StopCalc())
2463 			return B_OK;
2464 
2465 		if (loopControl->CheckUserCanceled())
2466 			return kUserCanceled;
2467 
2468 		StatStruct statbuf;
2469 		status_t status = entry.GetStat(&statbuf);
2470 		if (status != B_OK)
2471 			return status;
2472 
2473 		(*_runningSize) += statbuf.st_blocks * 512;
2474 
2475 		if (S_ISDIR(statbuf.st_mode)) {
2476 			BDirectory subdir(&entry);
2477 			(*_dirCount)++;
2478 			status = FSRecursiveCalcSize(window, loopControl, &subdir,
2479 				_runningSize, _fileCount, _dirCount);
2480 			if (status != B_OK)
2481 				return status;
2482 		} else
2483 			(*_fileCount)++;
2484 	}
2485 	return B_OK;
2486 }
2487 
2488 
2489 status_t
2490 CalcItemsAndSize(CopyLoopControl* loopControl,
2491 	BObjectList<entry_ref>* refList, ssize_t blockSize, int32* totalCount,
2492 	off_t* totalSize)
2493 {
2494 	int32 fileCount = 0;
2495 	int32 dirCount = 0;
2496 
2497 	// check block size for sanity
2498 	if (blockSize < 0) {
2499 		// This would point at an error to retrieve the block size from
2500 		// the target volume. The code below cannot be used, it is only
2501 		// meant to get the block size when item operations happen on
2502 		// the source volume.
2503 		blockSize = 2048;
2504 	} else if (blockSize < 1024) {
2505 		blockSize = 1024;
2506 		if (entry_ref* ref = refList->ItemAt(0)) {
2507 			// TODO: This assumes all entries in the list share the same
2508 			// volume...
2509 			BVolume volume(ref->device);
2510 			if (volume.InitCheck() == B_OK)
2511 				blockSize = volume.BlockSize();
2512 		}
2513 	}
2514 	// File systems like ReiserFS may advertize a large block size, but
2515 	// stuff is still packed into blocks, so clamp maximum block size.
2516 	if (blockSize > 8192)
2517 		blockSize = 8192;
2518 
2519 	int32 num_items = refList->CountItems();
2520 	for (int32 i = 0; i < num_items; i++) {
2521 		entry_ref* ref = refList->ItemAt(i);
2522 		BEntry entry(ref);
2523 		StatStruct statbuf;
2524 		entry.GetStat(&statbuf);
2525 
2526 		if (loopControl->CheckUserCanceled())
2527 			return kUserCanceled;
2528 
2529 		if (S_ISDIR(statbuf.st_mode)) {
2530 			BDirectory dir(&entry);
2531 			dirCount++;
2532 			(*totalSize) += blockSize;
2533 			status_t result = FSRecursiveCalcSize(NULL, loopControl, &dir,
2534 				totalSize, &fileCount, &dirCount);
2535 			if (result != B_OK)
2536 				return result;
2537 		} else {
2538 			fileCount++;
2539 			(*totalSize) += statbuf.st_size + blockSize;
2540 		}
2541 	}
2542 
2543 	*totalCount += (fileCount + dirCount);
2544 	return B_OK;
2545 }
2546 
2547 
2548 status_t
2549 FSGetTrashDir(BDirectory* trashDir, dev_t dev)
2550 {
2551 	if (trashDir == NULL)
2552 		return B_BAD_VALUE;
2553 
2554 	BVolume volume(dev);
2555 	status_t result = volume.InitCheck();
2556 	if (result != B_OK)
2557 		return result;
2558 
2559 	BPath path;
2560 	result = find_directory(B_TRASH_DIRECTORY, &path, false, &volume);
2561 	if (result != B_OK)
2562 		return result;
2563 
2564 	result = trashDir->SetTo(path.Path());
2565 	if (result != B_OK) {
2566 		// Trash directory does not exist yet, create it.
2567 		result = create_directory(path.Path(), 0755);
2568 		if (result != B_OK)
2569 			return result;
2570 
2571 		result = trashDir->SetTo(path.Path());
2572 		if (result != B_OK)
2573 			return result;
2574 
2575 		// make Trash directory invisible
2576 		StatStruct sbuf;
2577 		trashDir->GetStat(&sbuf);
2578 
2579 		PoseInfo poseInfo;
2580 		poseInfo.fInvisible = true;
2581 		poseInfo.fInitedDirectory = sbuf.st_ino;
2582 		trashDir->WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &poseInfo,
2583 			sizeof(PoseInfo));
2584 	}
2585 
2586 	// set Trash icons (if they haven't already been set)
2587 	attr_info attrInfo;
2588 	size_t size;
2589 	const void* data;
2590 	if (trashDir->GetAttrInfo(kAttrLargeIcon, &attrInfo) == B_ENTRY_NOT_FOUND) {
2591 		data = GetTrackerResources()->LoadResource('ICON', R_TrashIcon, &size);
2592 		if (data != NULL)
2593 			trashDir->WriteAttr(kAttrLargeIcon, 'ICON', 0, data, size);
2594 	}
2595 
2596 	if (trashDir->GetAttrInfo(kAttrMiniIcon, &attrInfo) == B_ENTRY_NOT_FOUND) {
2597 		data = GetTrackerResources()->LoadResource('MICN', R_TrashIcon, &size);
2598 		if (data != NULL)
2599 			trashDir->WriteAttr(kAttrMiniIcon, 'MICN', 0, data, size);
2600 	}
2601 
2602 	if (trashDir->GetAttrInfo(kAttrIcon, &attrInfo) == B_ENTRY_NOT_FOUND) {
2603 		data = GetTrackerResources()->LoadResource(B_VECTOR_ICON_TYPE,
2604 			R_TrashIcon, &size);
2605 		if (data != NULL)
2606 			trashDir->WriteAttr(kAttrIcon, B_VECTOR_ICON_TYPE, 0, data, size);
2607 	}
2608 
2609 	return B_OK;
2610 }
2611 
2612 
2613 status_t
2614 FSGetDeskDir(BDirectory* deskDir)
2615 {
2616 	if (deskDir == NULL)
2617 		return B_BAD_VALUE;
2618 
2619 	BPath path;
2620 	status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true);
2621 	if (result != B_OK)
2622 		return result;
2623 
2624 	result = deskDir->SetTo(path.Path());
2625 	if (result != B_OK)
2626 		return result;
2627 
2628 	// set Desktop icons (if they haven't already been set)
2629 	attr_info attrInfo;
2630 	size_t size;
2631 	const void* data;
2632 	if (deskDir->GetAttrInfo(kAttrLargeIcon, &attrInfo) == B_ENTRY_NOT_FOUND) {
2633 		data = GetTrackerResources()->LoadResource('ICON', R_DeskIcon, &size);
2634 		if (data != NULL)
2635 			deskDir->WriteAttr(kAttrLargeIcon, 'ICON', 0, data, size);
2636 	}
2637 
2638 	if (deskDir->GetAttrInfo(kAttrMiniIcon, &attrInfo) == B_ENTRY_NOT_FOUND) {
2639 		data = GetTrackerResources()->LoadResource('MICN', R_DeskIcon, &size);
2640 		if (data != NULL)
2641 			deskDir->WriteAttr(kAttrMiniIcon, 'MICN', 0, data, size);
2642 	}
2643 
2644 	if (deskDir->GetAttrInfo(kAttrIcon, &attrInfo) == B_ENTRY_NOT_FOUND) {
2645 		data = GetTrackerResources()->LoadResource(B_VECTOR_ICON_TYPE,
2646 			R_DeskIcon, &size);
2647 		if (data != NULL)
2648 			deskDir->WriteAttr(kAttrIcon, B_VECTOR_ICON_TYPE, 0, data, size);
2649 	}
2650 
2651 	return B_OK;
2652 }
2653 
2654 
2655 status_t
2656 FSGetBootDeskDir(BDirectory* deskDir)
2657 {
2658 	BVolume bootVolume;
2659 	BVolumeRoster().GetBootVolume(&bootVolume);
2660 	BPath path;
2661 
2662 	status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true,
2663 		&bootVolume);
2664 	if (result != B_OK)
2665 		return result;
2666 
2667 	return deskDir->SetTo(path.Path());
2668 }
2669 
2670 
2671 static bool
2672 FSIsDirFlavor(const BEntry* entry, directory_which directoryType)
2673 {
2674 	StatStruct dir_stat;
2675 	StatStruct entry_stat;
2676 	BVolume volume;
2677 	BPath path;
2678 
2679 	if (entry->GetStat(&entry_stat) != B_OK)
2680 		return false;
2681 
2682 	if (volume.SetTo(entry_stat.st_dev) != B_OK)
2683 		return false;
2684 
2685 	if (find_directory(directoryType, &path, false, &volume) != B_OK)
2686 		return false;
2687 
2688 	stat(path.Path(), &dir_stat);
2689 
2690 	return dir_stat.st_ino == entry_stat.st_ino
2691 		&& dir_stat.st_dev == entry_stat.st_dev;
2692 }
2693 
2694 
2695 bool
2696 FSIsPrintersDir(const BEntry* entry)
2697 {
2698 	return FSIsDirFlavor(entry, B_USER_PRINTERS_DIRECTORY);
2699 }
2700 
2701 
2702 bool
2703 FSIsTrashDir(const BEntry* entry)
2704 {
2705 	return FSIsDirFlavor(entry, B_TRASH_DIRECTORY);
2706 }
2707 
2708 
2709 bool
2710 FSIsDeskDir(const BEntry* entry)
2711 {
2712 	BPath path;
2713 	status_t result = find_directory(B_DESKTOP_DIRECTORY, &path, true);
2714 	if (result != B_OK)
2715 		return false;
2716 
2717 	BEntry entryToCompare(path.Path());
2718 	return entryToCompare == *entry;
2719 }
2720 
2721 
2722 bool
2723 FSIsHomeDir(const BEntry* entry)
2724 {
2725 	return FSIsDirFlavor(entry, B_USER_DIRECTORY);
2726 }
2727 
2728 
2729 bool
2730 FSIsRootDir(const BEntry* entry)
2731 {
2732 	BPath path(entry);
2733 	return path == "/";
2734 }
2735 
2736 
2737 bool
2738 DirectoryMatchesOrContains(const BEntry* entry, directory_which which)
2739 {
2740 	BPath path;
2741 	if (find_directory(which, &path, false, NULL) != B_OK)
2742 		return false;
2743 
2744 	BEntry dirEntry(path.Path());
2745 	if (dirEntry.InitCheck() != B_OK)
2746 		return false;
2747 
2748 	if (dirEntry == *entry)
2749 		// root level match
2750 		return true;
2751 
2752 	BDirectory dir(&dirEntry);
2753 	return dir.Contains(entry);
2754 }
2755 
2756 
2757 bool
2758 DirectoryMatchesOrContains(const BEntry* entry, const char* additionalPath,
2759 	directory_which which)
2760 {
2761 	BPath path;
2762 	if (find_directory(which, &path, false, NULL) != B_OK)
2763 		return false;
2764 
2765 	path.Append(additionalPath);
2766 	BEntry dirEntry(path.Path());
2767 	if (dirEntry.InitCheck() != B_OK)
2768 		return false;
2769 
2770 	if (dirEntry == *entry)
2771 		// root level match
2772 		return true;
2773 
2774 	BDirectory dir(&dirEntry);
2775 	return dir.Contains(entry);
2776 }
2777 
2778 
2779 bool
2780 DirectoryMatches(const BEntry* entry, directory_which which)
2781 {
2782 	BPath path;
2783 	if (find_directory(which, &path, false, NULL) != B_OK)
2784 		return false;
2785 
2786 	BEntry dirEntry(path.Path());
2787 	if (dirEntry.InitCheck() != B_OK)
2788 		return false;
2789 
2790 	return dirEntry == *entry;
2791 }
2792 
2793 
2794 bool
2795 DirectoryMatches(const BEntry* entry, const char* additionalPath,
2796 	directory_which which)
2797 {
2798 	BPath path;
2799 	if (find_directory(which, &path, false, NULL) != B_OK)
2800 		return false;
2801 
2802 	path.Append(additionalPath);
2803 	BEntry dirEntry(path.Path());
2804 	if (dirEntry.InitCheck() != B_OK)
2805 		return false;
2806 
2807 	return dirEntry == *entry;
2808 }
2809 
2810 
2811 extern status_t
2812 FSFindTrackerSettingsDir(BPath* path, bool autoCreate)
2813 {
2814 	status_t result = find_directory(B_USER_SETTINGS_DIRECTORY, path,
2815 		autoCreate);
2816 	if (result != B_OK)
2817 		return result;
2818 
2819 	path->Append("Tracker");
2820 
2821 	return mkdir(path->Path(), 0777) ? B_OK : errno;
2822 }
2823 
2824 
2825 bool
2826 FSInTrashDir(const entry_ref* ref)
2827 {
2828 	BEntry entry(ref);
2829 	if (entry.InitCheck() != B_OK)
2830 		return false;
2831 
2832 	BDirectory trashDir;
2833 	if (FSGetTrashDir(&trashDir, ref->device) != B_OK)
2834 		return false;
2835 
2836 	return trashDir.Contains(&entry);
2837 }
2838 
2839 
2840 void
2841 FSEmptyTrash()
2842 {
2843 	if (find_thread("_tracker_empty_trash_") != B_OK) {
2844 		resume_thread(spawn_thread(empty_trash, "_tracker_empty_trash_",
2845 			B_NORMAL_PRIORITY, NULL));
2846 	}
2847 }
2848 
2849 
2850 status_t
2851 empty_trash(void*)
2852 {
2853 	// empty trash on all mounted volumes
2854 	status_t status = B_OK;
2855 
2856 	TrackerCopyLoopControl loopControl(kTrashState);
2857 
2858 	// calculate the sum total of all items on all volumes in trash
2859 	BObjectList<entry_ref> srcList;
2860 	int32 totalCount = 0;
2861 	off_t totalSize = 0;
2862 
2863 	BVolumeRoster volumeRoster;
2864 	BVolume volume;
2865 	while (volumeRoster.GetNextVolume(&volume) == B_OK) {
2866 		if (volume.IsReadOnly() || !volume.IsPersistent())
2867 			continue;
2868 
2869 		BDirectory trashDirectory;
2870 		if (FSGetTrashDir(&trashDirectory, volume.Device()) != B_OK)
2871 			continue;
2872 
2873 		BEntry entry;
2874 		trashDirectory.GetEntry(&entry);
2875 
2876 		entry_ref ref;
2877 		entry.GetRef(&ref);
2878 		srcList.AddItem(&ref);
2879 		status = CalcItemsAndSize(&loopControl, &srcList, volume.BlockSize(),
2880 			&totalCount, &totalSize);
2881 		if (status != B_OK)
2882 			break;
2883 
2884 		srcList.MakeEmpty();
2885 
2886 		// don't count trash directory itself
2887 		totalCount--;
2888 	}
2889 
2890 	if (status == B_OK) {
2891 		loopControl.Init(totalCount, totalCount);
2892 
2893 		volumeRoster.Rewind();
2894 		while (volumeRoster.GetNextVolume(&volume) == B_OK) {
2895 			if (volume.IsReadOnly() || !volume.IsPersistent())
2896 				continue;
2897 
2898 			BDirectory trashDirectory;
2899 			if (FSGetTrashDir(&trashDirectory, volume.Device()) != B_OK)
2900 				continue;
2901 
2902 			BEntry entry;
2903 			trashDirectory.GetEntry(&entry);
2904 			status = FSDeleteFolder(&entry, &loopControl, true, false);
2905 		}
2906 	}
2907 
2908 	if (status != B_OK && status != kTrashCanceled && status != kUserCanceled) {
2909 		BAlert* alert = new BAlert("", B_TRANSLATE("Error emptying Trash"),
2910 			B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
2911 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
2912 			alert->Go();
2913 	}
2914 
2915 	return B_OK;
2916 }
2917 
2918 
2919 status_t
2920 _DeleteTask(BObjectList<entry_ref>* list, bool confirm)
2921 {
2922 	if (confirm) {
2923 		bool dontMoveToTrash = TrackerSettings().DontMoveFilesToTrash();
2924 
2925 		if (!dontMoveToTrash) {
2926 			BAlert* alert = new BAlert("",
2927 				B_TRANSLATE_NOCOLLECT(kDeleteConfirmationStr),
2928 				B_TRANSLATE("Cancel"), B_TRANSLATE("Move to Trash"),
2929 				B_TRANSLATE("Delete"), B_WIDTH_AS_USUAL, B_OFFSET_SPACING,
2930 				B_WARNING_ALERT);
2931 
2932 			alert->SetShortcut(0, B_ESCAPE);
2933 			alert->SetShortcut(1, 'm');
2934 			alert->SetShortcut(2, 'd');
2935 
2936 			switch (alert->Go()) {
2937 				case 0:
2938 					delete list;
2939 					return B_OK;
2940 				case 1:
2941 					FSMoveToTrash(list, NULL, false);
2942 					return B_OK;
2943 			}
2944 		} else {
2945 			BAlert* alert = new BAlert("",
2946 				B_TRANSLATE_NOCOLLECT(kDeleteConfirmationStr),
2947 				B_TRANSLATE("Cancel"), B_TRANSLATE("Delete"), NULL,
2948 				B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_WARNING_ALERT);
2949 
2950 			alert->SetShortcut(0, B_ESCAPE);
2951 			alert->SetShortcut(1, 'd');
2952 
2953 			if (!alert->Go()) {
2954 				delete list;
2955 				return B_OK;
2956 			}
2957 		}
2958 	}
2959 
2960 	TrackerCopyLoopControl loopControl(kDeleteState);
2961 
2962 	// calculate the sum total of all items on all volumes in trash
2963 	int32 totalItems = 0;
2964 	int64 totalSize = 0;
2965 
2966 	status_t status = CalcItemsAndSize(&loopControl, list, 0, &totalItems,
2967 		&totalSize);
2968 	if (status == B_OK) {
2969 		loopControl.Init(totalItems, totalItems);
2970 
2971 		int32 count = list->CountItems();
2972 		for (int32 index = 0; index < count; index++) {
2973 			entry_ref ref(*list->ItemAt(index));
2974 			BEntry entry(&ref);
2975 			loopControl.UpdateStatus(ref.name, ref, 1, true);
2976 			if (entry.IsDirectory())
2977 				status = FSDeleteFolder(&entry, &loopControl, true, true, true);
2978 			else
2979 				status = entry.Remove();
2980 		}
2981 
2982 		if (status != kTrashCanceled && status != kUserCanceled
2983 			&& status != B_OK) {
2984 			BAlert* alert = new BAlert("", B_TRANSLATE("Error deleting items"),
2985 				B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL,
2986 				B_WARNING_ALERT);
2987 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
2988 			alert->Go();
2989 		}
2990 	}
2991 
2992 	delete list;
2993 
2994 	return B_OK;
2995 }
2996 
2997 status_t
2998 FSRecursiveCreateFolder(BPath path)
2999 {
3000 	BEntry entry(path.Path());
3001 	if (entry.InitCheck() != B_OK) {
3002 		BPath parentPath;
3003 		status_t err = path.GetParent(&parentPath);
3004 		if (err != B_OK)
3005 			return err;
3006 
3007 		err = FSRecursiveCreateFolder(parentPath);
3008 		if (err != B_OK)
3009 			return err;
3010 	}
3011 
3012 	entry.SetTo(path.Path());
3013 	if (entry.Exists())
3014 		return B_FILE_EXISTS;
3015 
3016 	BDirectory parent;
3017 	entry.GetParent(&parent);
3018 	parent.CreateDirectory(entry.Name(), NULL);
3019 
3020 	return B_OK;
3021 }
3022 
3023 status_t
3024 _RestoreTask(BObjectList<entry_ref>* list)
3025 {
3026 	TrackerCopyLoopControl loopControl(kRestoreFromTrashState);
3027 
3028 	// calculate the sum total of all items that will be restored
3029 	int32 totalItems = 0;
3030 	int64 totalSize = 0;
3031 
3032 	status_t err = CalcItemsAndSize(&loopControl, list, 0, &totalItems,
3033 		&totalSize);
3034 	if (err == B_OK) {
3035 		loopControl.Init(totalItems, totalItems);
3036 
3037 		int32 count = list->CountItems();
3038 		for (int32 index = 0; index < count; index++) {
3039 			entry_ref ref(*list->ItemAt(index));
3040 			BEntry entry(&ref);
3041 			BPath originalPath;
3042 
3043 			loopControl.UpdateStatus(ref.name, ref, 1, true);
3044 
3045 			if (FSGetOriginalPath(&entry, &originalPath) != B_OK)
3046 				continue;
3047 
3048 			BEntry originalEntry(originalPath.Path());
3049 			BPath parentPath;
3050 			err = originalPath.GetParent(&parentPath);
3051 			if (err != B_OK)
3052 				continue;
3053 			BEntry parentEntry(parentPath.Path());
3054 
3055 			if (parentEntry.InitCheck() != B_OK || !parentEntry.Exists()) {
3056 				if (FSRecursiveCreateFolder(parentPath) == B_OK) {
3057 					originalEntry.SetTo(originalPath.Path());
3058 					if (entry.InitCheck() != B_OK)
3059 						continue;
3060 				}
3061 			}
3062 
3063 			if (!originalEntry.Exists()) {
3064 				BDirectory dir(parentPath.Path());
3065 				if (dir.InitCheck() == B_OK) {
3066 					const char* leafName = originalEntry.Name();
3067 					if (entry.MoveTo(&dir, leafName) == B_OK) {
3068 						BNode node(&entry);
3069 						if (node.InitCheck() == B_OK)
3070 							node.RemoveAttr(kAttrOriginalPath);
3071 					}
3072 				}
3073 			}
3074 
3075 			err = loopControl.CheckUserCanceled();
3076 			if (err != B_OK)
3077 				break;
3078 		}
3079 	}
3080 
3081 	delete list;
3082 
3083 	return err;
3084 }
3085 
3086 void
3087 FSCreateTrashDirs()
3088 {
3089 	BVolume volume;
3090 	BVolumeRoster roster;
3091 
3092 	roster.Rewind();
3093 	while (roster.GetNextVolume(&volume) == B_OK) {
3094 		if (volume.IsReadOnly() || !volume.IsPersistent())
3095 			continue;
3096 
3097 		BDirectory trashDir;
3098 		FSGetTrashDir(&trashDir, volume.Device());
3099 	}
3100 }
3101 
3102 
3103 status_t
3104 FSCreateNewFolder(const entry_ref* ref)
3105 {
3106 	node_ref node;
3107 	node.device = ref->device;
3108 	node.node = ref->directory;
3109 
3110 	BDirectory dir(&node);
3111 	status_t result = dir.InitCheck();
3112 	if (result != B_OK)
3113 		return result;
3114 
3115 	// ToDo: is that really necessary here?
3116 	BString name(ref->name);
3117 	FSMakeOriginalName(name, &dir, "-");
3118 
3119 	BDirectory newDir;
3120 	result = dir.CreateDirectory(name.String(), &newDir);
3121 	if (result != B_OK)
3122 		return result;
3123 
3124 	BNodeInfo nodeInfo(&newDir);
3125 	nodeInfo.SetType(B_DIR_MIMETYPE);
3126 
3127 	return result;
3128 }
3129 
3130 
3131 status_t
3132 FSCreateNewFolderIn(const node_ref* dirNode, entry_ref* newRef,
3133 	node_ref* newNode)
3134 {
3135 	BDirectory dir(dirNode);
3136 	status_t result = dir.InitCheck();
3137 	if (result == B_OK) {
3138 		char name[B_FILE_NAME_LENGTH];
3139 		strlcpy(name, B_TRANSLATE("New folder"), sizeof(name));
3140 
3141 		int32 fnum = 1;
3142 		while (dir.Contains(name)) {
3143 			// if base name already exists then add a number
3144 			// TODO: move this logic to FSMakeOriginalName
3145 			if (++fnum > 9) {
3146 				snprintf(name, sizeof(name), B_TRANSLATE("New folder%ld"),
3147 					fnum);
3148 			} else {
3149 				snprintf(name, sizeof(name), B_TRANSLATE("New folder %ld"),
3150 					fnum);
3151 			}
3152 		}
3153 
3154 		BDirectory newDir;
3155 		result = dir.CreateDirectory(name, &newDir);
3156 		if (result == B_OK) {
3157 			BEntry entry;
3158 			newDir.GetEntry(&entry);
3159 			entry.GetRef(newRef);
3160 			entry.GetNodeRef(newNode);
3161 
3162 			BNodeInfo nodeInfo(&newDir);
3163 			nodeInfo.SetType(B_DIR_MIMETYPE);
3164 
3165 			// add undo item
3166 			NewFolderUndo undo(*newRef);
3167 			return B_OK;
3168 		}
3169 	}
3170 
3171 	BAlert* alert = new BAlert("",
3172 		B_TRANSLATE("Sorry, could not create a new folder."),
3173 		B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
3174 	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
3175 	alert->Go();
3176 
3177 	return result;
3178 }
3179 
3180 
3181 ReadAttrResult
3182 ReadAttr(const BNode* node, const char* hostAttrName,
3183 	const char* foreignAttrName, type_code type, off_t offset, void* buffer,
3184 	size_t length, void (*swapFunc)(void*), bool isForeign)
3185 {
3186 	if (!isForeign && node->ReadAttr(hostAttrName, type, offset, buffer,
3187 			length) == (ssize_t)length) {
3188 		return kReadAttrNativeOK;
3189 	}
3190 
3191 	// PRINT(("trying %s\n", foreignAttrName));
3192 	// try the other endianness
3193 	if (node->ReadAttr(foreignAttrName, type, offset, buffer, length)
3194 			!= (ssize_t)length) {
3195 		return kReadAttrFailed;
3196 	}
3197 
3198 	// PRINT(("got %s\n", foreignAttrName));
3199 	if (!swapFunc)
3200 		return kReadAttrForeignOK;
3201 
3202 	(swapFunc)(buffer);
3203 		// run the endian swapper
3204 
3205 	return kReadAttrForeignOK;
3206 }
3207 
3208 
3209 ReadAttrResult
3210 GetAttrInfo(const BNode* node, const char* hostAttrName,
3211 	const char* foreignAttrName, type_code* type, size_t* size)
3212 {
3213 	attr_info info;
3214 
3215 	if (node->GetAttrInfo(hostAttrName, &info) == B_OK) {
3216 		if (type)
3217 			*type = info.type;
3218 		if (size)
3219 			*size = (size_t)info.size;
3220 
3221 		return kReadAttrNativeOK;
3222 	}
3223 
3224 	if (node->GetAttrInfo(foreignAttrName, &info) == B_OK) {
3225 		if (type)
3226 			*type = info.type;
3227 		if (size)
3228 			*size = (size_t)info.size;
3229 
3230 		return kReadAttrForeignOK;
3231 	}
3232 	return kReadAttrFailed;
3233 }
3234 
3235 
3236 status_t
3237 FSGetParentVirtualDirectoryAware(const BEntry& entry, entry_ref& _ref)
3238 {
3239 	node_ref nodeRef;
3240 	if (entry.GetNodeRef(&nodeRef) == B_OK) {
3241 		if (VirtualDirectoryManager* manager
3242 				= VirtualDirectoryManager::Instance()) {
3243 			AutoLocker<VirtualDirectoryManager> managerLocker(manager);
3244 			if (manager->GetParentDirectoryDefinitionFile(nodeRef, _ref,
3245 					nodeRef)) {
3246 				return B_OK;
3247 			}
3248 		}
3249 	}
3250 
3251 	status_t error;
3252 	BDirectory parent;
3253 	BEntry parentEntry;
3254 	if ((error = entry.GetParent(&parent)) != B_OK
3255 		|| (error = parent.GetEntry(&parentEntry)) != B_OK
3256 		|| (error = parentEntry.GetRef(&_ref)) != B_OK) {
3257 		return error;
3258 	}
3259 
3260 	return B_OK;
3261 }
3262 
3263 
3264 status_t
3265 FSGetParentVirtualDirectoryAware(const BEntry& entry, BEntry& _entry)
3266 {
3267 	node_ref nodeRef;
3268 	if (entry.GetNodeRef(&nodeRef) == B_OK) {
3269 		if (VirtualDirectoryManager* manager
3270 				= VirtualDirectoryManager::Instance()) {
3271 			AutoLocker<VirtualDirectoryManager> managerLocker(manager);
3272 			entry_ref parentRef;
3273 			if (manager->GetParentDirectoryDefinitionFile(nodeRef, parentRef,
3274 					nodeRef)) {
3275 				return _entry.SetTo(&parentRef);
3276 			}
3277 		}
3278 	}
3279 
3280 	return entry.GetParent(&_entry);
3281 }
3282 
3283 
3284 status_t
3285 FSGetParentVirtualDirectoryAware(const BEntry& entry, BNode& _node)
3286 {
3287 	entry_ref ref;
3288 	status_t error = FSGetParentVirtualDirectoryAware(entry, ref);
3289 	if (error == B_OK)
3290 		error = _node.SetTo(&ref);
3291 
3292 	return error;
3293 }
3294 
3295 
3296 // launching code
3297 
3298 static status_t
3299 TrackerOpenWith(const BMessage* refs)
3300 {
3301 	BMessage clone(*refs);
3302 
3303 	ASSERT(dynamic_cast<TTracker*>(be_app) != NULL);
3304 	ASSERT(clone.what != 0);
3305 
3306 	clone.AddInt32("launchUsingSelector", 0);
3307 	// runs the Open With window
3308 	be_app->PostMessage(&clone);
3309 
3310 	return B_OK;
3311 }
3312 
3313 
3314 static void
3315 AsynchLaunchBinder(void (*func)(const entry_ref*, const BMessage*, bool on),
3316 	const entry_ref* appRef, const BMessage* refs, bool openWithOK)
3317 {
3318 	BMessage* task = new BMessage;
3319 	task->AddPointer("function", (void*)func);
3320 	task->AddMessage("refs", refs);
3321 	task->AddBool("openWithOK", openWithOK);
3322 	if (appRef != NULL)
3323 		task->AddRef("appRef", appRef);
3324 
3325 	extern BLooper* gLaunchLooper;
3326 	gLaunchLooper->PostMessage(task);
3327 }
3328 
3329 
3330 static bool
3331 SniffIfGeneric(const entry_ref* ref)
3332 {
3333 	BNode node(ref);
3334 	char type[B_MIME_TYPE_LENGTH];
3335 	BNodeInfo info(&node);
3336 	if (info.GetType(type) == B_OK
3337 		&& strcasecmp(type, B_FILE_MIME_TYPE) != 0) {
3338 		// already has a type and it's not octet stream
3339 		return false;
3340 	}
3341 
3342 	BPath path(ref);
3343 	if (path.Path()) {
3344 		// force a mimeset
3345 		node.RemoveAttr(kAttrMIMEType);
3346 		update_mime_info(path.Path(), 0, 1, 1);
3347 	}
3348 
3349 	return true;
3350 }
3351 
3352 
3353 static void
3354 SniffIfGeneric(const BMessage* refs)
3355 {
3356 	entry_ref ref;
3357 	for (int32 index = 0; ; index++) {
3358 		if (refs->FindRef("refs", index, &ref) != B_OK)
3359 			break;
3360 		SniffIfGeneric(&ref);
3361 	}
3362 }
3363 
3364 
3365 static void
3366 _TrackerLaunchAppWithDocuments(const entry_ref* appRef, const BMessage* refs,
3367 	bool openWithOK)
3368 {
3369 	team_id team;
3370 
3371 	status_t error = B_ERROR;
3372 	BString alertString;
3373 
3374 	for (int32 mimesetIt = 0; ; mimesetIt++) {
3375 		error = be_roster->Launch(appRef, refs, &team);
3376 		if (error == B_ALREADY_RUNNING)
3377 			// app already running, not really an error
3378 			error = B_OK;
3379 
3380 		if (error == B_OK)
3381 			break;
3382 
3383 		if (mimesetIt > 0)
3384 			break;
3385 
3386 		// failed to open, try mimesetting the refs and launching again
3387 		SniffIfGeneric(refs);
3388 	}
3389 
3390 	if (error == B_OK) {
3391 		// close possible parent window, if specified
3392 		const node_ref* nodeToClose = 0;
3393 		ssize_t numBytes;
3394 		if (refs != NULL && refs->FindData("nodeRefsToClose", B_RAW_TYPE,
3395 				(const void**)&nodeToClose, &numBytes) == B_OK
3396 			&& nodeToClose != NULL) {
3397 			TTracker* tracker = dynamic_cast<TTracker*>(be_app);
3398 			if (tracker != NULL)
3399 				tracker->CloseParent(*nodeToClose);
3400 		}
3401 	} else {
3402 		alertString.SetTo(B_TRANSLATE("Could not open \"%name\" (%error). "));
3403 		alertString.ReplaceFirst("%name", appRef->name);
3404 		alertString.ReplaceFirst("%error", strerror(error));
3405 		if (refs != NULL && openWithOK && error != B_SHUTTING_DOWN) {
3406 			alertString << B_TRANSLATE_NOCOLLECT(kFindAlternativeStr);
3407 			BAlert* alert = new BAlert("", alertString.String(),
3408 				B_TRANSLATE("Cancel"), B_TRANSLATE("Find"), 0,
3409 				B_WIDTH_AS_USUAL, B_WARNING_ALERT);
3410 			alert->SetShortcut(0, B_ESCAPE);
3411 			if (alert->Go() == 1)
3412 				error = TrackerOpenWith(refs);
3413 		} else {
3414 			BAlert* alert = new BAlert("", alertString.String(),
3415 				B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL,
3416 				B_WARNING_ALERT);
3417 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
3418 			alert->Go();
3419 		}
3420 	}
3421 }
3422 
3423 
3424 extern "C" char** environ;
3425 
3426 
3427 static status_t
3428 LoaderErrorDetails(const entry_ref* app, BString &details)
3429 {
3430 	BPath path;
3431 	BEntry appEntry(app, true);
3432 
3433 	status_t result = appEntry.GetPath(&path);
3434 	if (result != B_OK)
3435 		return result;
3436 
3437 	char* argv[2] = { const_cast<char*>(path.Path()), 0};
3438 
3439 	port_id errorPort = create_port(1, "Tracker loader error");
3440 
3441 	// count environment variables
3442 	int32 envCount = 0;
3443 	while (environ[envCount] != NULL)
3444 		envCount++;
3445 
3446 	char** flatArgs = NULL;
3447 	size_t flatArgsSize;
3448 	result = __flatten_process_args((const char**)argv, 1,
3449 		environ, &envCount, argv[0], &flatArgs, &flatArgsSize);
3450 	if (result != B_OK)
3451 		return result;
3452 
3453 	result = _kern_load_image(flatArgs, flatArgsSize, 1, envCount,
3454 		B_NORMAL_PRIORITY, B_WAIT_TILL_LOADED, errorPort, 0);
3455 	if (result == B_OK) {
3456 		// we weren't supposed to be able to start the application...
3457 		return B_ERROR;
3458 	}
3459 
3460 	// read error message from port and construct details string
3461 
3462 	ssize_t bufferSize;
3463 
3464 	do {
3465 		bufferSize = port_buffer_size_etc(errorPort, B_RELATIVE_TIMEOUT, 0);
3466 	} while (bufferSize == B_INTERRUPTED);
3467 
3468 	if (bufferSize <= B_OK) {
3469 		delete_port(errorPort);
3470 		return bufferSize;
3471 	}
3472 
3473 	uint8* buffer = (uint8*)malloc(bufferSize);
3474 	if (buffer == NULL) {
3475 		delete_port(errorPort);
3476 		return B_NO_MEMORY;
3477 	}
3478 
3479 	bufferSize = read_port_etc(errorPort, NULL, buffer, bufferSize,
3480 		B_RELATIVE_TIMEOUT, 0);
3481 	delete_port(errorPort);
3482 
3483 	if (bufferSize < B_OK) {
3484 		free(buffer);
3485 		return bufferSize;
3486 	}
3487 
3488 	BMessage message;
3489 	result = message.Unflatten((const char*)buffer);
3490 	free(buffer);
3491 
3492 	if (result != B_OK)
3493 		return result;
3494 
3495 	int32 errorCode = B_ERROR;
3496 	result = message.FindInt32("error", &errorCode);
3497 	if (result != B_OK)
3498 		return result;
3499 
3500 	const char* detailName = NULL;
3501 	switch (errorCode) {
3502 		case B_MISSING_LIBRARY:
3503 			detailName = "missing library";
3504 			break;
3505 
3506 		case B_MISSING_SYMBOL:
3507 			detailName = "missing symbol";
3508 			break;
3509 	}
3510 
3511 	if (detailName == NULL)
3512 		return B_ERROR;
3513 
3514 	const char* detail;
3515 	for (int32 i = 0; message.FindString(detailName, i, &detail) == B_OK;
3516 			i++) {
3517 		if (i > 0)
3518 			details += ", ";
3519 		details += detail;
3520 	}
3521 
3522 	return B_OK;
3523 }
3524 
3525 
3526 static void
3527 _TrackerLaunchDocuments(const entry_ref*, const BMessage* refs,
3528 	bool openWithOK)
3529 {
3530 	if (refs == NULL)
3531 		return;
3532 
3533 	BMessage copyOfRefs(*refs);
3534 
3535 	entry_ref documentRef;
3536 	if (copyOfRefs.FindRef("refs", &documentRef) != B_OK) {
3537 		// nothing to launch, we are done
3538 		return;
3539 	}
3540 
3541 	status_t error = B_ERROR;
3542 	entry_ref app;
3543 	BMessage* refsToPass = NULL;
3544 	BString alertString;
3545 	const char* alternative = 0;
3546 
3547 	for (int32 mimesetIt = 0; ; mimesetIt++) {
3548 		alertString = "";
3549 		error = be_roster->FindApp(&documentRef, &app);
3550 
3551 		if (error != B_OK && mimesetIt == 0) {
3552 			SniffIfGeneric(&copyOfRefs);
3553 			continue;
3554 		}
3555 
3556 		if (error != B_OK) {
3557 			alertString.SetTo(B_TRANSLATE("Could not find an application to "
3558 				"open \"%name\" (%error). "));
3559 			alertString.ReplaceFirst("%name", documentRef.name);
3560 			alertString.ReplaceFirst("%error", strerror(error));
3561 			if (openWithOK)
3562 				alternative = B_TRANSLATE_NOCOLLECT(kFindApplicationStr);
3563 
3564 			break;
3565 		} else {
3566 			BEntry appEntry(&app, true);
3567 			for (int32 index = 0;;) {
3568 				// remove the app itself from the refs received so we don't
3569 				// try to open ourselves
3570 				entry_ref ref;
3571 				if (copyOfRefs.FindRef("refs", index, &ref) != B_OK)
3572 					break;
3573 
3574 				// deal with symlinks properly
3575 				BEntry documentEntry(&ref, true);
3576 				if (appEntry == documentEntry) {
3577 					PRINT(("stripping %s, app %s \n", ref.name, app.name));
3578 					copyOfRefs.RemoveData("refs", index);
3579 				} else {
3580 					PRINT(("leaving %s, app %s  \n", ref.name, app.name));
3581 					index++;
3582 				}
3583 			}
3584 
3585 			refsToPass = CountRefs(&copyOfRefs) > 0 ? &copyOfRefs: 0;
3586 			team_id team;
3587 			error = be_roster->Launch(&app, refsToPass, &team);
3588 			if (error == B_ALREADY_RUNNING)
3589 				// app already running, not really an error
3590 				error = B_OK;
3591 			if (error == B_OK || mimesetIt != 0)
3592 				break;
3593 			if (error == B_LAUNCH_FAILED_EXECUTABLE) {
3594 				BVolume volume(documentRef.device);
3595 				if (volume.IsReadOnly()) {
3596 					BMimeType type;
3597 					error = BMimeType::GuessMimeType(&documentRef, &type);
3598 					if (error != B_OK)
3599 						break;
3600 					error = be_roster->FindApp(type.Type(), &app);
3601 					if (error != B_OK)
3602 						break;
3603 					error = be_roster->Launch(&app, refs, &team);
3604 					if (error == B_ALREADY_RUNNING)
3605 						// app already running, not really an error
3606 						error = B_OK;
3607 					if (error == B_OK || mimesetIt != 0)
3608 						break;
3609 				}
3610 			}
3611 
3612 			SniffIfGeneric(&copyOfRefs);
3613 		}
3614 	}
3615 
3616 	if (error != B_OK && alertString.Length() == 0) {
3617 		BString loaderErrorString;
3618 		bool openedDocuments = true;
3619 
3620 		if (!refsToPass) {
3621 			// we just double clicked the app itself, do not offer to
3622 			// find a handling app
3623 			openWithOK = false;
3624 			openedDocuments = false;
3625 		}
3626 		if (error == B_UNKNOWN_EXECUTABLE && !refsToPass) {
3627 			// We know it's an executable, but something unsupported
3628 			alertString.SetTo(B_TRANSLATE("\"%name\" is an unsupported "
3629 				"executable."));
3630 			alertString.ReplaceFirst("%name", app.name);
3631 		} else if (error == B_LEGACY_EXECUTABLE && !refsToPass) {
3632 			// For the moment, this marks an old R3 binary, we may want to
3633 			// extend it to gcc2 binaries someday post R1
3634 			alertString.SetTo(B_TRANSLATE("\"%name\" is a legacy executable. "
3635 				"Please obtain an updated version or recompile "
3636 				"the application."));
3637 			alertString.ReplaceFirst("%name", app.name);
3638 		} else if (error == B_LAUNCH_FAILED_EXECUTABLE && !refsToPass) {
3639 			alertString.SetTo(B_TRANSLATE("Could not open \"%name\". "
3640 				"The file is mistakenly marked as executable. "));
3641 			alertString.ReplaceFirst("%name", app.name);
3642 
3643 			if (!openWithOK) {
3644 				// offer the possibility to change the permissions
3645 
3646 				alertString << B_TRANSLATE("\nShould this be fixed?");
3647 				BAlert* alert = new BAlert("", alertString.String(),
3648 					B_TRANSLATE("Cancel"), B_TRANSLATE("Proceed"), 0,
3649 					B_WIDTH_AS_USUAL, B_WARNING_ALERT);
3650 				alert->SetShortcut(0, B_ESCAPE);
3651 				if (alert->Go() == 1) {
3652 					BEntry entry(&documentRef);
3653 					mode_t permissions;
3654 
3655 					error = entry.GetPermissions(&permissions);
3656 					if (error == B_OK) {
3657 						error = entry.SetPermissions(permissions
3658 							& ~(S_IXUSR | S_IXGRP | S_IXOTH));
3659 					}
3660 					if (error == B_OK) {
3661 						// we updated the permissions, so let's try again
3662 						_TrackerLaunchDocuments(NULL, refs, false);
3663 						return;
3664 					} else {
3665 						alertString.SetTo(B_TRANSLATE("Could not update "
3666 							"permissions of file \"%name\". %error"));
3667 						alertString.ReplaceFirst("%name", app.name);
3668 						alertString.ReplaceFirst("%error", strerror(error));
3669 					}
3670 				} else
3671 					return;
3672 			}
3673 
3674 			alternative = B_TRANSLATE_NOCOLLECT(kFindApplicationStr);
3675 		} else if (error == B_LAUNCH_FAILED_APP_IN_TRASH) {
3676 			alertString.SetTo(B_TRANSLATE("Could not open \"%document\" "
3677 				"because application \"%app\" is in the Trash. "));
3678 			alertString.ReplaceFirst("%document", documentRef.name);
3679 			alertString.ReplaceFirst("%app", app.name);
3680 			alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr);
3681 		} else if (error == B_LAUNCH_FAILED_APP_NOT_FOUND) {
3682 			alertString.SetTo(
3683 				B_TRANSLATE("Could not open \"%name\" (%error). "));
3684 			alertString.ReplaceFirst("%name", documentRef.name);
3685 			alertString.ReplaceFirst("%error", strerror(error));
3686 			alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr);
3687 		} else if (error == B_MISSING_SYMBOL
3688 			&& LoaderErrorDetails(&app, loaderErrorString) == B_OK) {
3689 			if (openedDocuments) {
3690 				alertString.SetTo(B_TRANSLATE("Could not open \"%document\" "
3691 					"with application \"%app\" (Missing symbol: %symbol). "
3692 					"\n"));
3693 				alertString.ReplaceFirst("%document", documentRef.name);
3694 				alertString.ReplaceFirst("%app", app.name);
3695 				alertString.ReplaceFirst("%symbol",
3696 					loaderErrorString.String());
3697 			} else {
3698 				alertString.SetTo(B_TRANSLATE("Could not open \"%document\" "
3699 					"(Missing symbol: %symbol). \n"));
3700 				alertString.ReplaceFirst("%document", documentRef.name);
3701 				alertString.ReplaceFirst("%symbol",
3702 					loaderErrorString.String());
3703 			}
3704 			alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr);
3705 		} else if (error == B_MISSING_LIBRARY
3706 			&& LoaderErrorDetails(&app, loaderErrorString) == B_OK) {
3707 			if (openedDocuments) {
3708 				alertString.SetTo(B_TRANSLATE("Could not open \"%document\" "
3709 					"with application \"%app\" (Missing libraries: %library). "
3710 					"\n"));
3711 				alertString.ReplaceFirst("%document", documentRef.name);
3712 				alertString.ReplaceFirst("%app", app.name);
3713 				alertString.ReplaceFirst("%library",
3714 					loaderErrorString.String());
3715 			} else {
3716 				alertString.SetTo(B_TRANSLATE("Could not open \"%document\" "
3717 					"(Missing libraries: %library). \n"));
3718 				alertString.ReplaceFirst("%document", documentRef.name);
3719 				alertString.ReplaceFirst("%library",
3720 					loaderErrorString.String());
3721 			}
3722 			alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr);
3723 		} else {
3724 			alertString.SetTo(B_TRANSLATE("Could not open \"%document\" with "
3725 				"application \"%app\" (%error). "));
3726 				alertString.ReplaceFirst("%document", documentRef.name);
3727 				alertString.ReplaceFirst("%app", app.name);
3728 				alertString.ReplaceFirst("%error", strerror(error));
3729 			alternative = B_TRANSLATE_NOCOLLECT(kFindAlternativeStr);
3730 		}
3731 	}
3732 
3733 	if (error != B_OK) {
3734 		if (openWithOK) {
3735 			ASSERT(alternative);
3736 			alertString << alternative;
3737 			BAlert* alert = new BAlert("", alertString.String(),
3738 				B_TRANSLATE("Cancel"), B_TRANSLATE("Find"), 0,
3739 				B_WIDTH_AS_USUAL, B_WARNING_ALERT);
3740 			alert->SetShortcut(0, B_ESCAPE);
3741 			if (alert->Go() == 1)
3742 				error = TrackerOpenWith(refs);
3743 		} else {
3744 			BAlert* alert = new BAlert("", alertString.String(),
3745 				B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL,
3746 				B_WARNING_ALERT);
3747 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
3748 			alert->Go();
3749 		}
3750 	}
3751 }
3752 
3753 // the following three calls don't return any reasonable error codes,
3754 // should fix that, making them void
3755 
3756 status_t
3757 TrackerLaunch(const entry_ref* appRef, const BMessage* refs, bool async,
3758 	bool openWithOK)
3759 {
3760 	if (!async)
3761 		_TrackerLaunchAppWithDocuments(appRef, refs, openWithOK);
3762 	else {
3763 		AsynchLaunchBinder(&_TrackerLaunchAppWithDocuments, appRef, refs,
3764 			openWithOK);
3765 	}
3766 
3767 	return B_OK;
3768 }
3769 
3770 status_t
3771 TrackerLaunch(const entry_ref* appRef, bool async)
3772 {
3773 	if (!async)
3774 		_TrackerLaunchAppWithDocuments(appRef, NULL, false);
3775 	else
3776 		AsynchLaunchBinder(&_TrackerLaunchAppWithDocuments, appRef, 0, false);
3777 
3778 	return B_OK;
3779 }
3780 
3781 status_t
3782 TrackerLaunch(const BMessage* refs, bool async, bool openWithOK)
3783 {
3784 	if (!async)
3785 		_TrackerLaunchDocuments(NULL, refs, openWithOK);
3786 	else
3787 		AsynchLaunchBinder(&_TrackerLaunchDocuments, NULL, refs, openWithOK);
3788 
3789 	return B_OK;
3790 }
3791 
3792 
3793 // external launch calls; need to be robust, work if Tracker is not running
3794 
3795 
3796 #if !B_BEOS_VERSION_DANO
3797 _IMPEXP_TRACKER
3798 #endif
3799 status_t
3800 FSLaunchItem(const entry_ref* application, const BMessage* refsReceived,
3801 	bool async, bool openWithOK)
3802 {
3803 	return TrackerLaunch(application, refsReceived, async, openWithOK);
3804 }
3805 
3806 
3807 #if !B_BEOS_VERSION_DANO
3808 _IMPEXP_TRACKER
3809 #endif
3810 status_t
3811 FSOpenWith(BMessage* listOfRefs)
3812 {
3813 	status_t result = B_ERROR;
3814 	listOfRefs->what = B_REFS_RECEIVED;
3815 
3816 	if (dynamic_cast<TTracker*>(be_app) != NULL)
3817 		result = TrackerOpenWith(listOfRefs);
3818 	else
3819 		ASSERT(!"not yet implemented");
3820 
3821 	return result;
3822 }
3823 
3824 
3825 // legacy calls, need for compatibility
3826 
3827 
3828 void
3829 FSOpenWithDocuments(const entry_ref* executable, BMessage* documents)
3830 {
3831 	TrackerLaunch(executable, documents, true);
3832 	delete documents;
3833 }
3834 
3835 
3836 status_t
3837 FSLaunchUsing(const entry_ref* ref, BMessage* listOfRefs)
3838 {
3839 	BMessage temp(B_REFS_RECEIVED);
3840 	if (listOfRefs == NULL) {
3841 		ASSERT(ref != NULL);
3842 		temp.AddRef("refs", ref);
3843 		listOfRefs = &temp;
3844 	}
3845 	FSOpenWith(listOfRefs);
3846 
3847 	return B_OK;
3848 }
3849 
3850 
3851 status_t
3852 FSLaunchItem(const entry_ref* appRef, BMessage* refs, int32, bool async)
3853 {
3854 	if (refs != NULL)
3855 		refs->what = B_REFS_RECEIVED;
3856 
3857 	status_t result = TrackerLaunch(appRef, refs, async, true);
3858 	delete refs;
3859 
3860 	return result;
3861 }
3862 
3863 
3864 void
3865 FSLaunchItem(const entry_ref* appRef, BMessage* refs, int32 workspace)
3866 {
3867 	FSLaunchItem(appRef, refs, workspace, true);
3868 }
3869 
3870 
3871 // Get the original path of an entry in the trash
3872 status_t
3873 FSGetOriginalPath(BEntry* entry, BPath* result)
3874 {
3875 	status_t err;
3876 	entry_ref ref;
3877 	err = entry->GetRef(&ref);
3878 	if (err != B_OK)
3879 		return err;
3880 
3881 	// Only call the routine for entries in the trash
3882 	if (!FSInTrashDir(&ref))
3883 		return B_ERROR;
3884 
3885 	BNode node(entry);
3886 	BString originalPath;
3887 	if (node.ReadAttrString(kAttrOriginalPath, &originalPath) == B_OK) {
3888 		// We're in luck, the entry has the original path in an attribute
3889 		err = result->SetTo(originalPath.String());
3890 		return err;
3891 	}
3892 
3893 	// Iterate the parent directories to find one with
3894 	// the original path attribute
3895 	BEntry parent(*entry);
3896 	err = parent.InitCheck();
3897 	if (err != B_OK)
3898 		return err;
3899 
3900 	// walk up the directory structure until we find a node
3901 	// with original path attribute
3902 	do {
3903 		// move to the parent of this node
3904 		err = parent.GetParent(&parent);
3905 		if (err != B_OK)
3906 			return err;
3907 
3908 		// return if we are at the root of the trash
3909 		if (FSIsTrashDir(&parent))
3910 			return B_ENTRY_NOT_FOUND;
3911 
3912 		// get the parent as a node
3913 		err = node.SetTo(&parent);
3914 		if (err != B_OK)
3915 			return err;
3916 	} while (node.ReadAttrString(kAttrOriginalPath, &originalPath) != B_OK);
3917 
3918 	// Found the attribute, figure out there this file
3919 	// used to live, based on the successfully-read attribute
3920 	err = result->SetTo(originalPath.String());
3921 	if (err != B_OK)
3922 		return err;
3923 
3924 	BPath path, pathParent;
3925 	err = parent.GetPath(&pathParent);
3926 	if (err != B_OK)
3927 		return err;
3928 
3929 	err = entry->GetPath(&path);
3930 	if (err != B_OK)
3931 		return err;
3932 
3933 	result->Append(path.Path() + strlen(pathParent.Path()) + 1);
3934 		// compute the new path by appending the offset of
3935 		// the item we are locating, to the original path
3936 		// of the parent
3937 
3938 	return B_OK;
3939 }
3940 
3941 
3942 directory_which
3943 WellKnowEntryList::Match(const node_ref* node)
3944 {
3945 	const WellKnownEntry* result = MatchEntry(node);
3946 	if (result != NULL)
3947 		return result->which;
3948 
3949 	return (directory_which)-1;
3950 }
3951 
3952 
3953 const WellKnowEntryList::WellKnownEntry*
3954 WellKnowEntryList::MatchEntry(const node_ref* node)
3955 {
3956 	if (self == NULL)
3957 		self = new WellKnowEntryList();
3958 
3959 	return self->MatchEntryCommon(node);
3960 }
3961 
3962 
3963 const WellKnowEntryList::WellKnownEntry*
3964 WellKnowEntryList::MatchEntryCommon(const node_ref* node)
3965 {
3966 	uint32 count = entries.size();
3967 	for (uint32 index = 0; index < count; index++) {
3968 		if (*node == entries[index].node)
3969 			return &entries[index];
3970 	}
3971 
3972 	return NULL;
3973 }
3974 
3975 
3976 void
3977 WellKnowEntryList::Quit()
3978 {
3979 	delete self;
3980 	self = NULL;
3981 }
3982 
3983 
3984 void
3985 WellKnowEntryList::AddOne(directory_which which, const char* name)
3986 {
3987 	BPath path;
3988 	if (find_directory(which, &path, true) != B_OK)
3989 		return;
3990 
3991 	BEntry entry(path.Path(), true);
3992 	node_ref node;
3993 	if (entry.GetNodeRef(&node) != B_OK)
3994 		return;
3995 
3996 	entries.push_back(WellKnownEntry(&node, which, name));
3997 }
3998 
3999 
4000 void
4001 WellKnowEntryList::AddOne(directory_which which, directory_which base,
4002 	const char* extra, const char* name)
4003 {
4004 	BPath path;
4005 	if (find_directory(base, &path, true) != B_OK)
4006 		return;
4007 
4008 	path.Append(extra);
4009 	BEntry entry(path.Path(), true);
4010 	node_ref node;
4011 	if (entry.GetNodeRef(&node) != B_OK)
4012 		return;
4013 
4014 	entries.push_back(WellKnownEntry(&node, which, name));
4015 }
4016 
4017 
4018 void
4019 WellKnowEntryList::AddOne(directory_which which, const char* path,
4020 	const char* name)
4021 {
4022 	BEntry entry(path, true);
4023 	node_ref node;
4024 	if (entry.GetNodeRef(&node) != B_OK)
4025 		return;
4026 
4027 	entries.push_back(WellKnownEntry(&node, which, name));
4028 }
4029 
4030 
4031 WellKnowEntryList::WellKnowEntryList()
4032 {
4033 	AddOne(B_SYSTEM_DIRECTORY, "system");
4034 	AddOne((directory_which)B_BOOT_DISK, "/boot", "boot");
4035 	AddOne(B_USER_DIRECTORY, "home");
4036 
4037 	AddOne(B_BEOS_FONTS_DIRECTORY, "fonts");
4038 	AddOne(B_USER_FONTS_DIRECTORY, "fonts");
4039 
4040 	AddOne(B_BEOS_APPS_DIRECTORY, "apps");
4041 	AddOne(B_APPS_DIRECTORY, "apps");
4042 	AddOne((directory_which)B_USER_DESKBAR_APPS_DIRECTORY,
4043 		B_USER_DESKBAR_DIRECTORY, "Applications", "apps");
4044 
4045 	AddOne(B_BEOS_PREFERENCES_DIRECTORY, "preferences");
4046 	AddOne(B_PREFERENCES_DIRECTORY, "preferences");
4047 	AddOne((directory_which)B_USER_DESKBAR_PREFERENCES_DIRECTORY,
4048 		B_USER_DESKBAR_DIRECTORY, "Preferences", "preferences");
4049 
4050 	AddOne((directory_which)B_USER_MAIL_DIRECTORY, B_USER_DIRECTORY, "mail",
4051 		"mail");
4052 
4053 	AddOne((directory_which)B_USER_QUERIES_DIRECTORY, B_USER_DIRECTORY,
4054 		"queries", "queries");
4055 
4056 	AddOne(B_SYSTEM_DEVELOP_DIRECTORY, "develop");
4057 	AddOne((directory_which)B_USER_DESKBAR_DEVELOP_DIRECTORY,
4058 		B_USER_DESKBAR_DIRECTORY, "Development", "develop");
4059 
4060 	AddOne(B_USER_CONFIG_DIRECTORY, "config");
4061 
4062 	AddOne((directory_which)B_USER_PEOPLE_DIRECTORY, B_USER_DIRECTORY,
4063 		"people", "people");
4064 
4065 	AddOne((directory_which)B_USER_DOWNLOADS_DIRECTORY, B_USER_DIRECTORY,
4066 		"downloads", "downloads");
4067 }
4068 
4069 WellKnowEntryList* WellKnowEntryList::self = NULL;
4070 
4071 } // namespace BPrivate
4072