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