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