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