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