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