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