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