xref: /haiku/src/kits/tracker/Model.cpp (revision d0ac609964842f8cdb6d54b3c539c6c15293e172)
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 //	Dedicated to BModel
36 
37 // ToDo:
38 // Consider moving iconFrom logic to BPose
39 // use a more efficient way of storing file type and preferred app strings
40 
41 
42 #include "Model.h"
43 
44 #include <stdlib.h>
45 #include <strings.h>
46 
47 #include <fs_info.h>
48 #include <fs_attr.h>
49 
50 #include <AppDefs.h>
51 #include <Bitmap.h>
52 #include <Catalog.h>
53 #include <Debug.h>
54 #include <Directory.h>
55 #include <Entry.h>
56 #include <File.h>
57 #include <Locale.h>
58 #include <NodeInfo.h>
59 #include <NodeMonitor.h>
60 #include <Path.h>
61 #include <SymLink.h>
62 #include <Query.h>
63 #include <Volume.h>
64 #include <VolumeRoster.h>
65 
66 #include "Attributes.h"
67 #include "Bitmaps.h"
68 #include "FindPanel.h"
69 #include "FSUtils.h"
70 #include "MimeTypes.h"
71 #include "Tracker.h"
72 #include "Utilities.h"
73 
74 
75 #undef B_TRANSLATION_CONTEXT
76 #define B_TRANSLATION_CONTEXT "Model"
77 
78 
79 #ifdef CHECK_OPEN_MODEL_LEAKS
80 BObjectList<Model>* writableOpenModelList = NULL;
81 BObjectList<Model>* readOnlyOpenModelList = NULL;
82 #endif
83 
84 
85 namespace BPrivate {
86 extern
87 #ifdef _IMPEXP_BE
88 _IMPEXP_BE
89 #endif
90 bool CheckNodeIconHintPrivate(const BNode*, bool);
91 }	// namespace BPrivate
92 
93 
94 
95 
96 static bool
97 HasVectorIconHint(BNode* node)
98 {
99 	attr_info info;
100 	return node->GetAttrInfo(kAttrIcon, &info) == B_OK;
101 }
102 
103 
104 //	#pragma mark - Model()
105 
106 
107 Model::Model()
108 	:
109 	fPreferredAppName(NULL),
110 	fBaseType(kUnknownNode),
111 	fIconFrom(kUnknownSource),
112 	fWritable(false),
113 	fNode(NULL),
114 	fStatus(B_NO_INIT),
115 	fHasLocalizedName(false),
116 	fLocalizedNameIsCached(false)
117 {
118 }
119 
120 
121 Model::Model(const Model& other)
122 	:
123 	fEntryRef(other.fEntryRef),
124 	fMimeType(other.fMimeType),
125 	fPreferredAppName(NULL),
126 	fBaseType(other.fBaseType),
127 	fIconFrom(other.fIconFrom),
128 	fWritable(false),
129 	fNode(NULL),
130 	fLocalizedName(other.fLocalizedName),
131 	fHasLocalizedName(other.fHasLocalizedName),
132 	fLocalizedNameIsCached(other.fLocalizedNameIsCached)
133 {
134 	fStatBuf.st_dev = other.NodeRef()->device;
135 	fStatBuf.st_ino = other.NodeRef()->node;
136 
137 	if (other.IsSymLink() && other.LinkTo())
138 		fLinkTo = new Model(*other.LinkTo());
139 
140 	fStatus = OpenNode(other.IsNodeOpenForWriting());
141 	if (fStatus == B_OK) {
142 		ASSERT(fNode);
143 		fNode->GetStat(&fStatBuf);
144 		ASSERT(fStatBuf.st_dev == other.NodeRef()->device);
145 		ASSERT(fStatBuf.st_ino == other.NodeRef()->node);
146 	}
147 	if (!other.IsNodeOpen())
148 		CloseNode();
149 }
150 
151 
152 Model::Model(const node_ref* dirNode, const node_ref* node, const char* name,
153 	bool open, bool writable)
154 	:
155 	fPreferredAppName(NULL),
156 	fWritable(false),
157 	fNode(NULL),
158 	fHasLocalizedName(false),
159 	fLocalizedNameIsCached(false)
160 {
161 	SetTo(dirNode, node, name, open, writable);
162 }
163 
164 
165 Model::Model(const BEntry* entry, bool open, bool writable)
166 	:
167 	fPreferredAppName(NULL),
168 	fWritable(false),
169 	fNode(NULL),
170 	fHasLocalizedName(false),
171 	fLocalizedNameIsCached(false)
172 {
173 	SetTo(entry, open, writable);
174 }
175 
176 
177 Model::Model(const entry_ref* ref, bool traverse, bool open, bool writable)
178 	:
179 	fPreferredAppName(NULL),
180 	fBaseType(kUnknownNode),
181 	fIconFrom(kUnknownSource),
182 	fWritable(false),
183 	fNode(NULL),
184 	fHasLocalizedName(false),
185 	fLocalizedNameIsCached(false)
186 {
187 	BEntry entry(ref, traverse);
188 	fStatus = entry.InitCheck();
189 	if (fStatus == B_OK)
190 		SetTo(&entry, open, writable);
191 }
192 
193 
194 void
195 Model::DeletePreferredAppVolumeNameLinkTo()
196 {
197 	if (IsSymLink()) {
198 		Model* tmp = fLinkTo;
199 			// deal with link to link to self
200 		fLinkTo = NULL;
201 		delete tmp;
202 	} else if (IsVolume())
203 		free(fVolumeName);
204 	else
205 		free(fPreferredAppName);
206 
207 	fPreferredAppName = NULL;
208 }
209 
210 
211 Model::~Model()
212 {
213 #ifdef CHECK_OPEN_MODEL_LEAKS
214 	if (writableOpenModelList != NULL)
215 		writableOpenModelList->RemoveItem(this);
216 
217 	if (readOnlyOpenModelList != NULL)
218 		readOnlyOpenModelList->RemoveItem(this);
219 #endif
220 
221 	DeletePreferredAppVolumeNameLinkTo();
222 	if (IconCache::NeedsDeletionNotification((IconSource)fIconFrom)) {
223 		// this check allows us to use temporary Model in the IconCache
224 		// without the danger of a deadlock
225 		IconCache::sIconCache->Deleting(this);
226 	}
227 #if xDEBUG
228 	if (fNode != NULL)
229 		PRINT(("destructor closing node for %s\n", Name()));
230 #endif
231 
232 	delete fNode;
233 }
234 
235 
236 status_t
237 Model::SetTo(const BEntry* entry, bool open, bool writable)
238 {
239 	delete fNode;
240 	fNode = NULL;
241 	DeletePreferredAppVolumeNameLinkTo();
242 	fIconFrom = kUnknownSource;
243 	fBaseType = kUnknownNode;
244 	fMimeType = "";
245 
246 	fStatus = entry->GetRef(&fEntryRef);
247 	if (fStatus != B_OK)
248 		return fStatus;
249 
250 	fStatus = entry->GetStat(&fStatBuf);
251 	if (fStatus != B_OK)
252 		return fStatus;
253 
254 	fStatus = OpenNode(writable);
255 	if (!open)
256 		CloseNode();
257 
258 	return fStatus;
259 }
260 
261 
262 status_t
263 Model::SetTo(const entry_ref* newRef, bool traverse, bool open, bool writable)
264 {
265 	delete fNode;
266 	fNode = NULL;
267 	DeletePreferredAppVolumeNameLinkTo();
268 	fIconFrom = kUnknownSource;
269 	fBaseType = kUnknownNode;
270 	fMimeType = "";
271 
272 	BEntry tmpEntry(newRef, traverse);
273 	fStatus = tmpEntry.InitCheck();
274 	if (fStatus != B_OK)
275 		return fStatus;
276 
277 	if (traverse)
278 		tmpEntry.GetRef(&fEntryRef);
279 	else
280 		fEntryRef = *newRef;
281 
282 	fStatus = tmpEntry.GetStat(&fStatBuf);
283 	if (fStatus != B_OK)
284 		return fStatus;
285 
286 	fStatus = OpenNode(writable);
287 	if (!open)
288 		CloseNode();
289 
290 	return fStatus;
291 }
292 
293 
294 status_t
295 Model::SetTo(const node_ref* dirNode, const node_ref* nodeRef,
296 	const char* name, bool open, bool writable)
297 {
298 	delete fNode;
299 	fNode = NULL;
300 	DeletePreferredAppVolumeNameLinkTo();
301 	fIconFrom = kUnknownSource;
302 	fBaseType = kUnknownNode;
303 	fMimeType = "";
304 
305 	fStatBuf.st_dev = nodeRef->device;
306 	fStatBuf.st_ino = nodeRef->node;
307 	fEntryRef.device = dirNode->device;
308 	fEntryRef.directory = dirNode->node;
309 	fEntryRef.name = strdup(name);
310 
311 	BEntry tmpNode(&fEntryRef);
312 	fStatus = tmpNode.InitCheck();
313 	if (fStatus != B_OK)
314 		return fStatus;
315 
316 	fStatus = tmpNode.GetStat(&fStatBuf);
317 	if (fStatus != B_OK)
318 		return fStatus;
319 
320 	fStatus = OpenNode(writable);
321 
322 	if (!open)
323 		CloseNode();
324 
325 	return fStatus;
326 }
327 
328 
329 status_t
330 Model::InitCheck() const
331 {
332 	return fStatus;
333 }
334 
335 
336 int
337 Model::CompareFolderNamesFirst(const Model* compareModel) const
338 {
339 	if (compareModel == NULL)
340 		return -1;
341 
342 	const Model* resolvedCompareModel = compareModel->ResolveIfLink();
343 	const Model* resolvedMe = ResolveIfLink();
344 
345 	bool meIsDirOrVolume = resolvedMe->IsDirectory() || resolvedMe->IsVolume()
346 		|| resolvedMe->IsVirtualDirectory();
347 	bool otherIsDirOrVolume = resolvedCompareModel->IsDirectory()
348 		|| resolvedCompareModel->IsVolume()
349 		|| resolvedCompareModel->IsVirtualDirectory();
350 
351 	if (meIsDirOrVolume) {
352 		if (!otherIsDirOrVolume)
353 			return -1;
354 	} else if (otherIsDirOrVolume)
355 		return 1;
356 
357 	return NaturalCompare(Name(), compareModel->Name());
358 }
359 
360 
361 const char*
362 Model::Name() const
363 {
364 	static const char* kRootNodeName = B_TRANSLATE_MARK(B_DISKS_DIR_NAME);
365 	static const char* kTrashNodeName = B_TRANSLATE_MARK(B_TRASH_DIR_NAME);
366 	static const char* kDesktopNodeName = B_TRANSLATE_MARK(B_DESKTOP_DIR_NAME);
367 
368 	switch (fBaseType) {
369 		case kRootNode:
370 			return B_TRANSLATE_NOCOLLECT(kRootNodeName);
371 
372 		case kVolumeNode:
373 			if (fVolumeName != NULL)
374 				return fVolumeName;
375 			break;
376 
377 		case kTrashNode:
378 			return B_TRANSLATE_NOCOLLECT(kTrashNodeName);
379 
380 		case kDesktopNode:
381 			return B_TRANSLATE_NOCOLLECT(kDesktopNodeName);
382 
383 		default:
384 			break;
385 	}
386 
387 	if (fHasLocalizedName && gLocalizedNamePreferred)
388 		return fLocalizedName.String();
389 	else
390 		return fEntryRef.name;
391 }
392 
393 
394 status_t
395 Model::OpenNode(bool writable)
396 {
397 	if (IsNodeOpen() && (writable == IsNodeOpenForWriting()))
398 		return B_OK;
399 
400 	OpenNodeCommon(writable);
401 
402 	return fStatus;
403 }
404 
405 
406 status_t
407 Model::UpdateStatAndOpenNode(bool writable)
408 {
409 	if (IsNodeOpen() && (writable == IsNodeOpenForWriting()))
410 		return B_OK;
411 
412 	// try reading the stat structure again
413 	BEntry tmpEntry(&fEntryRef);
414 	fStatus = tmpEntry.InitCheck();
415 	if (fStatus != B_OK)
416 		return fStatus;
417 
418 	fStatus = tmpEntry.GetStat(&fStatBuf);
419 	if (fStatus != B_OK)
420 		return fStatus;
421 
422 	OpenNodeCommon(writable);
423 
424 	return fStatus;
425 }
426 
427 
428 status_t
429 Model::OpenNodeCommon(bool writable)
430 {
431 #if xDEBUG
432 	PRINT(("opening node for %s\n", Name()));
433 #endif
434 
435 #ifdef CHECK_OPEN_MODEL_LEAKS
436 	if (writableOpenModelList != NULL)
437 		writableOpenModelList->RemoveItem(this);
438 
439 	if (readOnlyOpenModelList != NULL)
440 		readOnlyOpenModelList->RemoveItem(this);
441 #endif
442 
443 	if (fBaseType == kUnknownNode)
444 		SetupBaseType();
445 
446 	switch (fBaseType) {
447 		case kPlainNode:
448 		case kExecutableNode:
449 		case kQueryNode:
450 		case kQueryTemplateNode:
451 		case kVirtualDirectoryNode:
452 			// open or reopen
453 			delete fNode;
454 			fNode = new BFile(&fEntryRef,
455 				(uint32)(writable ? O_RDWR : O_RDONLY));
456 			break;
457 
458 		case kDirectoryNode:
459 		case kVolumeNode:
460 		case kRootNode:
461 		case kTrashNode:
462 		case kDesktopNode:
463 			if (!IsNodeOpen())
464 				fNode = new BDirectory(&fEntryRef);
465 
466 			if (fBaseType == kDirectoryNode
467 				&& static_cast<BDirectory*>(fNode)->IsRootDirectory()) {
468 				// promote from directory to volume
469 				fBaseType = kVolumeNode;
470 			}
471 			break;
472 
473 		case kLinkNode:
474 			if (!IsNodeOpen()) {
475 				BEntry entry(&fEntryRef);
476 				fNode = new BSymLink(&entry);
477 			}
478 			break;
479 
480 		default:
481 #if DEBUG
482 			PrintToStream();
483 #endif
484 			TRESPASS();
485 				// this can only happen if GetStat failed before,
486 				// in which case we shouldn't be here
487 
488 			// ToDo: Obviously, we can also be here if the type could not
489 			// be determined, for example for block devices (so the TRESPASS()
490 			// macro shouldn't be used here)!
491 			return fStatus = B_ERROR;
492 	}
493 
494 	fStatus = fNode->InitCheck();
495 	if (fStatus != B_OK) {
496 		delete fNode;
497 		fNode = NULL;
498 		// original code snoozed an error here and returned B_OK
499 		return fStatus;
500 	}
501 
502 	fWritable = writable;
503 
504 	if (fMimeType.Length() <= 0)
505 		FinishSettingUpType();
506 
507 #ifdef CHECK_OPEN_MODEL_LEAKS
508 	if (fWritable) {
509 		if (!writableOpenModelList) {
510 			TRACE();
511 			writableOpenModelList = new BObjectList<Model>(100);
512 		}
513 		writableOpenModelList->AddItem(this);
514 	} else {
515 		if (!readOnlyOpenModelList) {
516 			TRACE();
517 			readOnlyOpenModelList = new BObjectList<Model>(100);
518 		}
519 		readOnlyOpenModelList->AddItem(this);
520 	}
521 #endif
522 
523 	if (gLocalizedNamePreferred)
524 		CacheLocalizedName();
525 
526 	return fStatus;
527 }
528 
529 
530 void
531 Model::CloseNode()
532 {
533 #if xDEBUG
534 	PRINT(("closing node for %s\n", Name()));
535 #endif
536 
537 #ifdef CHECK_OPEN_MODEL_LEAKS
538 	if (writableOpenModelList != NULL)
539 		writableOpenModelList->RemoveItem(this);
540 
541 	if (readOnlyOpenModelList != NULL)
542 		readOnlyOpenModelList->RemoveItem(this);
543 #endif
544 
545 	delete fNode;
546 	fNode = NULL;
547 }
548 
549 
550 bool
551 Model::IsNodeOpen() const
552 {
553 	return fNode != NULL;
554 }
555 
556 
557 
558 bool
559 Model::IsNodeOpenForWriting() const
560 {
561 	return fNode != NULL && fWritable;
562 }
563 
564 
565 void
566 Model::SetupBaseType()
567 {
568 	switch (fStatBuf.st_mode & S_IFMT) {
569 		case S_IFDIR:
570 			// folder
571 			fBaseType = kDirectoryNode;
572 			break;
573 
574 		case S_IFREG:
575 			// regular file
576 			if ((fStatBuf.st_mode & S_IXUSR) != 0) {
577 				// executable
578 				fBaseType = kExecutableNode;
579 			} else {
580 				// non-executable
581 				fBaseType = kPlainNode;
582 			}
583 			break;
584 
585 		case S_IFLNK:
586 			// symlink
587 			fBaseType = kLinkNode;
588 			break;
589 
590 		default:
591 			fBaseType = kUnknownNode;
592 			break;
593 	}
594 }
595 
596 
597 void
598 Model::CacheLocalizedName()
599 {
600 	if (!fLocalizedNameIsCached) {
601 		fLocalizedNameIsCached = true;
602 		if (BLocaleRoster::Default()->GetLocalizedFileName(
603 				fLocalizedName, fEntryRef, true) == B_OK)
604 			fHasLocalizedName = true;
605 		else
606 			fHasLocalizedName = false;
607 	}
608 }
609 
610 
611 void
612 Model::FinishSettingUpType()
613 {
614 	char mimeString[B_MIME_TYPE_LENGTH];
615 	BEntry entry;
616 
617 	// while we are reading the node, do a little
618 	// snooping to see if it even makes sense to look for a node-based
619 	// icon
620 	// This serves as a hint to the icon cache, allowing it to not hit the
621 	// disk again for models that do not have an icon defined by the node
622 	if (IsNodeOpen()
623 		&& fBaseType != kLinkNode
624 		&& !CheckNodeIconHintPrivate(fNode,
625 			dynamic_cast<TTracker*>(be_app) == NULL)
626 		&& !HasVectorIconHint(fNode)) {
627 			// when checking for the node icon hint, if we are libtracker,
628 			// only check for small icons - checking for the large icons
629 			// is a little more work for the filesystem and this will
630 			// speed up the test. This makes node icons only work if there
631 			// is a small and a large node icon on a file - for libtracker
632 			// that is not a problem though
633 		fIconFrom = kUnknownNotFromNode;
634 	}
635 
636 	if (fBaseType != kDirectoryNode
637 		&& fBaseType != kVolumeNode
638 		&& fBaseType != kLinkNode
639 		&& IsNodeOpen()) {
640 		BNodeInfo info(fNode);
641 
642 		// check if a specific mime type is set
643 		if (info.GetType(mimeString) == B_OK) {
644 			// node has a specific mime type
645 			fMimeType = mimeString;
646 			if (strcmp(mimeString, B_QUERY_MIMETYPE) == 0)
647 				fBaseType = kQueryNode;
648 			else if (strcmp(mimeString, B_QUERY_TEMPLATE_MIMETYPE) == 0)
649 				fBaseType = kQueryTemplateNode;
650 			else if (strcmp(mimeString, kVirtualDirectoryMimeType) == 0)
651 				fBaseType = kVirtualDirectoryNode;
652 
653 			if (info.GetPreferredApp(mimeString) == B_OK) {
654 				if (fPreferredAppName)
655 					DeletePreferredAppVolumeNameLinkTo();
656 
657 				if (mimeString[0])
658 					fPreferredAppName = strdup(mimeString);
659 			}
660 		}
661 	}
662 
663 	switch (fBaseType) {
664 		case kDirectoryNode:
665 			entry.SetTo(&fEntryRef);
666 			if (entry.InitCheck() == B_OK) {
667 				if (FSIsTrashDir(&entry))
668 					fBaseType = kTrashNode;
669 				else if (FSIsDeskDir(&entry))
670 					fBaseType = kDesktopNode;
671 			}
672 
673 			fMimeType = B_DIR_MIMETYPE;
674 				// should use a shared string here
675 			if (IsNodeOpen()) {
676 				BNodeInfo info(fNode);
677 				if (info.GetType(mimeString) == B_OK)
678 					fMimeType = mimeString;
679 
680 				if (fIconFrom == kUnknownNotFromNode
681 					&& WellKnowEntryList::Match(NodeRef())
682 						> (directory_which)-1) {
683 					// one of home, beos, system, boot, etc.
684 					fIconFrom = kTrackerSupplied;
685 				}
686 			}
687 			break;
688 
689 		case kVolumeNode:
690 		{
691 			if (NodeRef()->node == fEntryRef.directory
692 				&& NodeRef()->device == fEntryRef.device) {
693 				// promote from volume to file system root
694 				fBaseType = kRootNode;
695 				fMimeType = B_ROOT_MIMETYPE;
696 				break;
697 			}
698 
699 			// volumes have to have a B_VOLUME_MIMETYPE type
700 			fMimeType = B_VOLUME_MIMETYPE;
701 			if (fIconFrom == kUnknownNotFromNode) {
702 				if (WellKnowEntryList::Match(NodeRef()) > (directory_which)-1)
703 					fIconFrom = kTrackerSupplied;
704 				else
705 					fIconFrom = kVolume;
706 			}
707 
708 			char name[B_FILE_NAME_LENGTH];
709 			BVolume volume(NodeRef()->device);
710 			if (volume.InitCheck() == B_OK && volume.GetName(name) == B_OK) {
711 				if (fVolumeName != NULL)
712 					DeletePreferredAppVolumeNameLinkTo();
713 
714 				fVolumeName = strdup(name);
715 			}
716 #if DEBUG
717 			else
718 				PRINT(("get volume name failed for %s\n", fEntryRef.name));
719 #endif
720 			break;
721 		}
722 
723 		case kLinkNode:
724 			fMimeType = B_LINK_MIMETYPE;
725 				// should use a shared string here
726 			break;
727 
728 		case kExecutableNode:
729 			if (IsNodeOpen()) {
730 				char signature[B_MIME_TYPE_LENGTH];
731 				if (GetAppSignatureFromAttr(dynamic_cast<BFile*>(fNode),
732 						signature) == B_OK) {
733 					if (fPreferredAppName)
734 						DeletePreferredAppVolumeNameLinkTo();
735 
736 					if (signature[0])
737 						fPreferredAppName = strdup(signature);
738 				}
739 			}
740 			if (fMimeType.Length() <= 0)
741 				fMimeType = B_APP_MIME_TYPE;
742 					// should use a shared string here
743 			break;
744 
745 		default:
746 			if (fMimeType.Length() <= 0)
747 				fMimeType = B_FILE_MIMETYPE;
748 			break;
749 	}
750 }
751 
752 
753 void
754 Model::ResetIconFrom()
755 {
756 	BModelOpener opener(this);
757 
758 	if (InitCheck() != B_OK)
759 		return;
760 
761 	// mirror the logic from FinishSettingUpType
762 	if ((fBaseType == kDirectoryNode || fBaseType == kVolumeNode
763 			|| fBaseType == kTrashNode || fBaseType == kDesktopNode)
764 		&& !CheckNodeIconHintPrivate(fNode,
765 			dynamic_cast<TTracker*>(be_app) == NULL)) {
766 		BDirectory* directory = dynamic_cast<BDirectory*>(fNode);
767 		if (WellKnowEntryList::Match(NodeRef()) > (directory_which)-1) {
768 			fIconFrom = kTrackerSupplied;
769 			return;
770 		} else if (directory != NULL && directory->IsRootDirectory()) {
771 			fIconFrom = kVolume;
772 			return;
773 		}
774 	}
775 	fIconFrom = kUnknownSource;
776 }
777 
778 
779 const char*
780 Model::PreferredAppSignature() const
781 {
782 	if (IsVolume() || IsSymLink())
783 		return "";
784 
785 	return fPreferredAppName ? fPreferredAppName : "";
786 }
787 
788 
789 void
790 Model::SetPreferredAppSignature(const char* signature)
791 {
792 	ASSERT(!IsVolume() && !IsSymLink());
793 	ASSERT(signature != fPreferredAppName);
794 		// self assignment should not be an option
795 
796 	free(fPreferredAppName);
797 	if (signature)
798 		fPreferredAppName = strdup(signature);
799 	else
800 		fPreferredAppName = NULL;
801 }
802 
803 
804 const Model*
805 Model::ResolveIfLink() const
806 {
807 	if (!IsSymLink())
808 		return this;
809 
810 	if (!fLinkTo)
811 		return this;
812 
813 	return fLinkTo;
814 }
815 
816 
817 Model*
818 Model::ResolveIfLink()
819 {
820 	if (!IsSymLink())
821 		return this;
822 
823 	if (!fLinkTo)
824 		return this;
825 
826 	return fLinkTo;
827 }
828 
829 
830 void
831 Model::SetLinkTo(Model* model)
832 {
833 	ASSERT(IsSymLink());
834 	ASSERT(!fLinkTo || (fLinkTo != model));
835 
836 	delete fLinkTo;
837 	fLinkTo = model;
838 }
839 
840 
841 void
842 Model::GetPreferredAppForBrokenSymLink(BString &result)
843 {
844 	if (!IsSymLink() || LinkTo()) {
845 		result = "";
846 		return;
847 	}
848 
849 	BModelOpener opener(this);
850 	BNodeInfo info(fNode);
851 	status_t error
852 		= info.GetPreferredApp(result.LockBuffer(B_MIME_TYPE_LENGTH));
853 	result.UnlockBuffer();
854 
855 	if (error != B_OK) {
856 		// Tracker will have to do
857 		result = kTrackerSignature;
858 	}
859 }
860 
861 
862 //	#pragma mark - Node monitor updating methods
863 
864 
865 void
866 Model::UpdateEntryRef(const node_ref* dirNode, const char* name)
867 {
868 	if (IsVolume()) {
869 		if (fVolumeName != NULL)
870 			DeletePreferredAppVolumeNameLinkTo();
871 
872 		fVolumeName = strdup(name);
873 	}
874 
875 	fEntryRef.device = dirNode->device;
876 	fEntryRef.directory = dirNode->node;
877 
878 	if (fEntryRef.name != NULL && strcmp(fEntryRef.name, name) == 0)
879 		return;
880 
881 	fEntryRef.set_name(name);
882 }
883 
884 
885 status_t
886 Model::WatchVolumeAndMountPoint(uint32 , BHandler* target)
887 {
888 	ASSERT(IsVolume());
889 
890 	if (fEntryRef.name != NULL && fVolumeName != NULL
891 		&& strcmp(fEntryRef.name, "boot") == 0) {
892 		// watch mount point for boot volume
893 		BString bootMountPoint("/");
894 		bootMountPoint += fVolumeName;
895 		BEntry mountPointEntry(bootMountPoint.String());
896 		Model mountPointModel(&mountPointEntry);
897 
898 		TTracker::WatchNode(mountPointModel.NodeRef(),
899 			B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR, target);
900 	}
901 
902 	return TTracker::WatchNode(NodeRef(),
903 		B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR, target);
904 }
905 
906 
907 bool
908 Model::AttrChanged(const char* attrName)
909 {
910 	// called on an attribute changed node monitor
911 	// sync up cached values of mime type and preferred app and
912 	// return true if icon needs updating
913 
914 	ASSERT(IsNodeOpen());
915 	if (attrName != NULL
916 		&& (strcmp(attrName, kAttrIcon) == 0
917 			|| strcmp(attrName, kAttrMiniIcon) == 0
918 			|| strcmp(attrName, kAttrLargeIcon) == 0)) {
919 		return true;
920 	}
921 
922 	if (attrName == NULL
923 		|| strcmp(attrName, kAttrMIMEType) == 0
924 		|| strcmp(attrName, kAttrPreferredApp) == 0) {
925 		char mimeString[B_MIME_TYPE_LENGTH];
926 		BNodeInfo info(fNode);
927 		if (info.GetType(mimeString) != B_OK)
928 			fMimeType = "";
929 		else {
930 			// node has a specific mime type
931 			fMimeType = mimeString;
932 			if (!IsVolume() && !IsSymLink()
933 				&& info.GetPreferredApp(mimeString) == B_OK) {
934 				SetPreferredAppSignature(mimeString);
935 			}
936 		}
937 
938 #if xDEBUG
939 		if (fIconFrom != kNode) {
940 			PRINT(("%s, %s:updating icon because file type changed\n",
941 				Name(), attrName != NULL ? attrName : ""));
942 		} else {
943 			PRINT(("Not updating icon even though type changed "
944 				"because icon is from node.\n"));
945 		}
946 #endif
947 
948 		return fIconFrom != kNode;
949 			// update icon unless it is coming from a node
950 	}
951 
952 	return attrName == NULL;
953 }
954 
955 
956 bool
957 Model::StatChanged()
958 {
959 	ASSERT(IsNodeOpen());
960 	mode_t oldMode = fStatBuf.st_mode;
961 	fStatus = fNode->GetStat(&fStatBuf);
962 
963 	if (oldMode != fStatBuf.st_mode) {
964 		bool forWriting = IsNodeOpenForWriting();
965 		CloseNode();
966 		//SetupBaseType();
967 			// the node type can't change with a stat update...
968 		OpenNodeCommon(forWriting);
969 		return true;
970 	}
971 
972 	return false;
973 }
974 
975 
976 //	#pragma mark - Mime handling methods
977 
978 
979 bool
980 Model::IsDropTarget(const Model* forDocument, bool traverse) const
981 {
982 	switch (CanHandleDrops()) {
983 		case kCanHandle:
984 			return true;
985 
986 		case kCannotHandle:
987 			return false;
988 
989 		default:
990 			break;
991 	}
992 
993 	if (forDocument == NULL)
994 		return true;
995 
996 	if (traverse) {
997 		BEntry entry(forDocument->EntryRef(), true);
998 		if (entry.InitCheck() != B_OK)
999 			return false;
1000 
1001 		BFile file(&entry, O_RDONLY);
1002 		BNodeInfo mime(&file);
1003 
1004 		if (mime.InitCheck() != B_OK)
1005 			return false;
1006 
1007 		char mimeType[B_MIME_TYPE_LENGTH];
1008 		mime.GetType(mimeType);
1009 
1010 		return SupportsMimeType(mimeType, 0) != kDoesNotSupportType;
1011 	}
1012 
1013 	// do some mime-based matching
1014 	const char* documentMimeType = forDocument->MimeType();
1015 	if (documentMimeType == NULL)
1016 		return false;
1017 
1018 	return SupportsMimeType(documentMimeType, 0) != kDoesNotSupportType;
1019 }
1020 
1021 
1022 Model::CanHandleResult
1023 Model::CanHandleDrops() const
1024 {
1025 	if (IsDirectory()) {
1026 		// directories take anything
1027 		// resolve permissions here
1028 		return kCanHandle;
1029 	}
1030 
1031 	if (IsSymLink()) {
1032 		// descend into symlink and try again on it's target
1033 
1034 		BEntry entry(&fEntryRef, true);
1035 		if (entry.InitCheck() != B_OK)
1036 			return kCannotHandle;
1037 
1038 		if (entry == BEntry(EntryRef()))
1039 			// self-referencing link, avoid infinite recursion
1040 			return kCannotHandle;
1041 
1042 		Model model(&entry);
1043 		if (model.InitCheck() != B_OK)
1044 			return kCannotHandle;
1045 
1046 		return model.CanHandleDrops();
1047 	}
1048 
1049 	if (IsExecutable())
1050 		return kNeedToCheckType;
1051 
1052 	return kCannotHandle;
1053 }
1054 
1055 
1056 inline bool
1057 IsSuperHandlerSignature(const char* signature)
1058 {
1059 	return strcasecmp(signature, B_FILE_MIMETYPE) == 0;
1060 }
1061 
1062 
1063 enum {
1064 	kDontMatch = 0,
1065 	kMatchSupertype,
1066 	kMatch
1067 };
1068 
1069 
1070 static int32
1071 MatchMimeTypeString(/*const */BString* documentType, const char* handlerType)
1072 {
1073 	// perform a mime type wildcard match
1074 	// handler types of the form "text"
1075 	// handle every handled type with same supertype,
1076 	// for everything else a full string match is used
1077 
1078 	int32 supertypeOnlyLength = 0;
1079 	const char* tmp = strstr(handlerType, "/");
1080 
1081 	if (tmp == NULL) {
1082 		// no subtype - supertype string only
1083 		supertypeOnlyLength = (int32)strlen(handlerType);
1084 	}
1085 
1086 	if (supertypeOnlyLength) {
1087 		// compare just the supertype
1088 		tmp = strstr(documentType->String(), "/");
1089 		if (tmp && (tmp - documentType->String() == supertypeOnlyLength)) {
1090 			if (documentType->ICompare(handlerType, supertypeOnlyLength) == 0)
1091 				return kMatchSupertype;
1092 			else
1093 				return kDontMatch;
1094 		}
1095 	}
1096 
1097 	if (documentType->ICompare(handlerType) == 0)
1098 		return kMatch;
1099 
1100 	return kDontMatch;
1101 }
1102 
1103 
1104 int32
1105 Model::SupportsMimeType(const char* type, const BObjectList<BString>* list,
1106 	bool exactReason) const
1107 {
1108 	ASSERT((type == 0) != (list == 0));
1109 		// pass in one or the other
1110 
1111 	int32 result = kDoesNotSupportType;
1112 
1113 	BFile file(EntryRef(), O_RDONLY);
1114 	BAppFileInfo handlerInfo(&file);
1115 
1116 	BMessage message;
1117 	if (handlerInfo.GetSupportedTypes(&message) != B_OK)
1118 		return kDoesNotSupportType;
1119 
1120 	for (int32 index = 0; ; index++) {
1121 		// check if this model lists the type of dropped document as supported
1122 
1123 		const char* mimeSignature;
1124 		ssize_t bufferLength;
1125 
1126 		if (message.FindData("types", 'CSTR', index,
1127 				(const void**)&mimeSignature, &bufferLength)) {
1128 			return result;
1129 		}
1130 
1131 		if (IsSuperHandlerSignature(mimeSignature)) {
1132 			if (!exactReason)
1133 				return kSuperhandlerModel;
1134 
1135 			if (result == kDoesNotSupportType)
1136 				result = kSuperhandlerModel;
1137 		}
1138 
1139 		int32 match;
1140 
1141 		if (type != NULL || (list != NULL && list->IsEmpty())) {
1142 			BString typeString(type);
1143 			match = MatchMimeTypeString(&typeString, mimeSignature);
1144 		} else {
1145 			match = WhileEachListItem(const_cast<BObjectList<BString>*>(list),
1146 				MatchMimeTypeString, mimeSignature);
1147 			// const_cast shouldnt be here, have to have it until
1148 			// MW cleans up
1149 		}
1150 		if (match == kMatch)
1151 			// supports the actual type, it can't get any better
1152 			return kModelSupportsType;
1153 		else if (match == kMatchSupertype) {
1154 			if (!exactReason)
1155 				return kModelSupportsSupertype;
1156 
1157 			// we already know this model supports the file as a supertype,
1158 			// now find out if it matches the type
1159 			result = kModelSupportsSupertype;
1160 		}
1161 	}
1162 
1163 	return result;
1164 }
1165 
1166 
1167 bool
1168 Model::IsDropTargetForList(const BObjectList<BString>* list) const
1169 {
1170 	switch (CanHandleDrops()) {
1171 		case kCanHandle:
1172 			return true;
1173 
1174 		case kCannotHandle:
1175 			return false;
1176 
1177 		default:
1178 			break;
1179 	}
1180 
1181 	return SupportsMimeType(0, list) != kDoesNotSupportType;
1182 }
1183 
1184 
1185 bool
1186 Model::IsSuperHandler() const
1187 {
1188 	ASSERT(CanHandleDrops() == kNeedToCheckType);
1189 
1190 	BFile file(EntryRef(), O_RDONLY);
1191 	BAppFileInfo handlerInfo(&file);
1192 
1193 	BMessage message;
1194 	if (handlerInfo.GetSupportedTypes(&message) != B_OK)
1195 		return false;
1196 
1197 	for (int32 index = 0; ; index++) {
1198 		const char* mimeSignature;
1199 		ssize_t bufferLength;
1200 
1201 		if (message.FindData("types", 'CSTR', index,
1202 			(const void**)&mimeSignature, &bufferLength)) {
1203 			return false;
1204 		}
1205 
1206 		if (IsSuperHandlerSignature(mimeSignature))
1207 			return true;
1208 	}
1209 	return false;
1210 }
1211 
1212 
1213 void
1214 Model::GetEntry(BEntry* entry) const
1215 {
1216 	entry->SetTo(EntryRef());
1217 }
1218 
1219 
1220 void
1221 Model::GetPath(BPath* path) const
1222 {
1223 	BEntry entry(EntryRef());
1224 	entry.GetPath(path);
1225 }
1226 
1227 
1228 bool
1229 Model::Mimeset(bool force)
1230 {
1231 	BString oldType = MimeType();
1232 	BPath path;
1233 	GetPath(&path);
1234 
1235 	update_mime_info(path.Path(), 0, 1, force ? 2 : 0);
1236 	ModelNodeLazyOpener opener(this);
1237 	opener.OpenNode();
1238 	AttrChanged(NULL);
1239 
1240 	return !oldType.ICompare(MimeType());
1241 }
1242 
1243 
1244 ssize_t
1245 Model::WriteAttr(const char* attr, type_code type, off_t offset,
1246 	const void* buffer, size_t length)
1247 {
1248 	BModelWriteOpener opener(this);
1249 	if (!fNode)
1250 		return 0;
1251 
1252 	ssize_t result = fNode->WriteAttr(attr, type, offset, buffer, length);
1253 	return result;
1254 }
1255 
1256 
1257 ssize_t
1258 Model::WriteAttrKillForeign(const char* attr, const char* foreignAttr,
1259 	type_code type, off_t offset, const void* buffer, size_t length)
1260 {
1261 	BModelWriteOpener opener(this);
1262 	if (!fNode)
1263 		return 0;
1264 
1265 	ssize_t result = fNode->WriteAttr(attr, type, offset, buffer, length);
1266 	if (result == (ssize_t)length)
1267 		// nuke attribute in opposite endianness
1268 		fNode->RemoveAttr(foreignAttr);
1269 	return result;
1270 }
1271 
1272 
1273 status_t
1274 Model::GetLongVersionString(BString &result, version_kind kind)
1275 {
1276 	BFile file(EntryRef(), O_RDONLY);
1277 	status_t error = file.InitCheck();
1278 	if (error != B_OK)
1279 		return error;
1280 
1281 	BAppFileInfo info(&file);
1282 	error = info.InitCheck();
1283 	if (error != B_OK)
1284 		return error;
1285 
1286 	version_info version;
1287 	error = info.GetVersionInfo(&version, kind);
1288 	if (error != B_OK)
1289 		return error;
1290 
1291 	result = version.long_info;
1292 	return B_OK;
1293 }
1294 
1295 status_t
1296 Model::GetVersionString(BString &result, version_kind kind)
1297 {
1298 	BFile file(EntryRef(), O_RDONLY);
1299 	status_t error = file.InitCheck();
1300 	if (error != B_OK)
1301 		return error;
1302 
1303 	BAppFileInfo info(&file);
1304 	error = info.InitCheck();
1305 	if (error != B_OK)
1306 		return error;
1307 
1308 	version_info version;
1309 	error = info.GetVersionInfo(&version, kind);
1310 	if (error != B_OK)
1311 		return error;
1312 
1313 	result.SetToFormat("%" B_PRId32 ".%" B_PRId32 ".%" B_PRId32, version.major,
1314 		version.middle, version.minor);
1315 
1316 	return B_OK;
1317 }
1318 
1319 #if DEBUG
1320 
1321 void
1322 Model::PrintToStream(int32 level, bool deep)
1323 {
1324 	PRINT(("model name %s, entry name %s, inode %" B_PRIdINO ", dev %"
1325 		B_PRIdDEV ", directory inode %" B_PRIdINO "\n",
1326 		Name() ? Name() : "**empty name**",
1327 		EntryRef()->name ? EntryRef()->name : "**empty ref name**",
1328 		NodeRef()->node,
1329 		NodeRef()->device,
1330 		EntryRef()->directory));
1331 	PRINT(("type %s \n", MimeType()));
1332 
1333 	PRINT(("model type: "));
1334 	switch (fBaseType) {
1335 		case kPlainNode:
1336 			PRINT(("plain\n"));
1337 			break;
1338 
1339 		case kQueryNode:
1340 			PRINT(("query\n"));
1341 			break;
1342 
1343 		case kQueryTemplateNode:
1344 			PRINT(("query template\n"));
1345 			break;
1346 
1347 		case kExecutableNode:
1348 			PRINT(("exe\n"));
1349 			break;
1350 
1351 		case kDirectoryNode:
1352 		case kTrashNode:
1353 		case kDesktopNode:
1354 			PRINT(("dir\n"));
1355 			break;
1356 
1357 		case kLinkNode:
1358 			PRINT(("link\n"));
1359 			break;
1360 
1361 		case kRootNode:
1362 			PRINT(("root\n"));
1363 			break;
1364 
1365 		case kVolumeNode:
1366 			PRINT(("volume, name %s\n", fVolumeName ? fVolumeName : ""));
1367 			break;
1368 
1369 		case kVirtualDirectoryNode:
1370 			PRINT(("virtual directory\n"));
1371 			break;
1372 
1373 		default:
1374 			PRINT(("unknown\n"));
1375 			break;
1376 	}
1377 
1378 	if (level < 1)
1379 		return;
1380 
1381 	if (!IsVolume()) {
1382 		PRINT(("preferred app %s\n",
1383 			fPreferredAppName ? fPreferredAppName : ""));
1384 	}
1385 
1386 	PRINT(("icon from: "));
1387 	switch (IconFrom()) {
1388 		case kUnknownSource:
1389 			PRINT(("unknown\n"));
1390 			break;
1391 
1392 		case kUnknownNotFromNode:
1393 			PRINT(("unknown but not from a node\n"));
1394 			break;
1395 
1396 		case kTrackerDefault:
1397 			PRINT(("tracker default\n"));
1398 			break;
1399 
1400 		case kTrackerSupplied:
1401 			PRINT(("tracker supplied\n"));
1402 			break;
1403 
1404 		case kMetaMime:
1405 			PRINT(("metamime\n"));
1406 			break;
1407 
1408 		case kPreferredAppForType:
1409 			PRINT(("preferred app for type\n"));
1410 			break;
1411 
1412 		case kPreferredAppForNode:
1413 			PRINT(("preferred app for node\n"));
1414 			break;
1415 
1416 		case kNode:
1417 			PRINT(("node\n"));
1418 			break;
1419 
1420 		case kVolume:
1421 			PRINT(("volume\n"));
1422 			break;
1423 
1424 		default:
1425 			break;
1426 	}
1427 
1428 	PRINT(("model %s opened %s \n", !IsNodeOpen() ? "not " : "",
1429 		IsNodeOpenForWriting() ? "for writing" : ""));
1430 
1431 	if (IsNodeOpen()) {
1432 		node_ref nodeRef;
1433 		fNode->GetNodeRef(&nodeRef);
1434 		PRINT(("node ref of open Node %" B_PRIdINO " %" B_PRIdDEV "\n",
1435 			nodeRef.node, nodeRef.device));
1436 	}
1437 
1438 	if (deep && IsSymLink()) {
1439 		BEntry tmpEntry(EntryRef(), true);
1440 		Model tmp(&tmpEntry);
1441 		PRINT(("symlink to:\n"));
1442 		tmp.PrintToStream();
1443 	}
1444 	TrackIconSource(B_MINI_ICON);
1445 	TrackIconSource(B_LARGE_ICON);
1446 }
1447 
1448 
1449 void
1450 Model::TrackIconSource(icon_size size)
1451 {
1452 	PRINT(("tracking %s icon\n", size == B_LARGE_ICON ? "large" : "small"));
1453 	BRect rect;
1454 	if (size == B_MINI_ICON)
1455 		rect.Set(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1);
1456 	else
1457 		rect.Set(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1);
1458 
1459 	BBitmap bitmap(rect, B_CMAP8);
1460 
1461 	BModelOpener opener(this);
1462 
1463 	if (Node() == NULL) {
1464 		PRINT(("track icon error - no node\n"));
1465 		return;
1466 	}
1467 
1468 	if (IsSymLink()) {
1469 		PRINT(("tracking symlink icon\n"));
1470 		if (fLinkTo) {
1471 			fLinkTo->TrackIconSource(size);
1472 			return;
1473 		}
1474 	}
1475 
1476 	if (fBaseType == kVolumeNode) {
1477 		BVolume volume(NodeRef()->device);
1478 		status_t result = volume.GetIcon(&bitmap, size);
1479 		PRINT(("getting icon from volume %s\n", strerror(result)));
1480 	} else {
1481 		BNodeInfo nodeInfo(Node());
1482 
1483 		status_t err = nodeInfo.GetIcon(&bitmap, size);
1484 		if (err == B_OK) {
1485 			// file knew which icon to use, we are done
1486 			PRINT(("track icon - got icon from file\n"));
1487 			return;
1488 		}
1489 
1490 		char preferredApp[B_MIME_TYPE_LENGTH];
1491 		err = nodeInfo.GetPreferredApp(preferredApp);
1492 		if (err == B_OK && preferredApp[0]) {
1493 			BMimeType preferredAppType(preferredApp);
1494 			err = preferredAppType.GetIconForType(MimeType(), &bitmap, size);
1495 			if (err == B_OK) {
1496 				PRINT(
1497 					("track icon - got icon for type %s from preferred "
1498 					 "app %s for file\n", MimeType(), preferredApp));
1499 				return;
1500 			}
1501 		}
1502 
1503 		BMimeType mimeType(MimeType());
1504 		err = mimeType.GetIcon(&bitmap, size);
1505 		if (err == B_OK) {
1506 			// the system knew what icon to use for the type, we are done
1507 			PRINT(("track icon - signature %s, got icon from system\n",
1508 				MimeType()));
1509 			return;
1510 		}
1511 
1512 		err = mimeType.GetPreferredApp(preferredApp);
1513 		if (err != B_OK) {
1514 			// no preferred App for document, give up
1515 			PRINT(("track icon - signature %s, no prefered app, error %s\n",
1516 				MimeType(), strerror(err)));
1517 			return;
1518 		}
1519 
1520 		BMimeType preferredAppType(preferredApp);
1521 		err = preferredAppType.GetIconForType(MimeType(), &bitmap, size);
1522 		if (err == B_OK) {
1523 			// the preferred app knew icon to use for the type, we are done
1524 			PRINT(
1525 				("track icon - signature %s, got icon from preferred "
1526 				 "app %s\n", MimeType(), preferredApp));
1527 			return;
1528 		}
1529 		PRINT(
1530 			("track icon - signature %s, preferred app %s, no icon, "
1531 			 "error %s\n", MimeType(), preferredApp, strerror(err)));
1532 	}
1533 }
1534 
1535 #endif	// DEBUG
1536 
1537 #ifdef CHECK_OPEN_MODEL_LEAKS
1538 
1539 namespace BPrivate {
1540 
1541 #include <stdio.h>
1542 
1543 void
1544 DumpOpenModels(bool extensive)
1545 {
1546 	if (readOnlyOpenModelList) {
1547 		int32 count = readOnlyOpenModelList->CountItems();
1548 		printf("%ld models open read-only:\n", count);
1549 		printf("==========================\n");
1550 		for (int32 index = 0; index < count; index++) {
1551 			if (extensive) {
1552 				printf("---------------------------\n");
1553 				readOnlyOpenModelList->ItemAt(index)->PrintToStream();
1554 			} else
1555 				printf("%s\n", readOnlyOpenModelList->ItemAt(index)->Name());
1556 		}
1557 	}
1558 
1559 	if (writableOpenModelList) {
1560 		int32 count = writableOpenModelList->CountItems();
1561 		printf("%ld models open writable:\n", count);
1562 		printf("models open writable:\n");
1563 		printf("======================\n");
1564 		for (int32 index = 0; index < count; index++) {
1565 			if (extensive) {
1566 				printf("---------------------------\n");
1567 				writableOpenModelList->ItemAt(index)->PrintToStream();
1568 			} else
1569 				printf("%s\n", writableOpenModelList->ItemAt(index)->Name());
1570 		}
1571 	}
1572 }
1573 
1574 
1575 void
1576 InitOpenModelDumping()
1577 {
1578 	readOnlyOpenModelList = 0;
1579 	writableOpenModelList = 0;
1580 }
1581 
1582 }	// namespace BPrivate
1583 
1584 #endif	// CHECK_OPEN_MODEL_LEAKS
1585