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