HaikuDepot: Custom List Removal (Depots)

Remove use of custom list class where it is not
really required in the area of Depots.  Also
convert the use of Depots to be wrapped in
BReference to match other model objects. At the
same time some data-loading logic has been
simplified.

Relates To #15534

Change-Id: Ie6fcc35f258a0c69c44990e4b09f6c32ec79945d
Reviewed-on: https://review.haiku-os.org/c/haiku/+/3225
Reviewed-by: Rene Gollent <[email protected]>
This commit is contained in:
Andrew Lindesay
2020-09-21 20:49:37 +00:00
parent a22fa0c977
commit 05880d133e
14 changed files with 175 additions and 206 deletions
+7
View File
@@ -35,6 +35,13 @@
#define HDTRACE(M...) HDLOG(LOG_LEVEL_TRACE, M)
#define HDERROR(M...) HDLOG(LOG_LEVEL_ERROR, M)
#define HDFATAL(M...) do { \
printf("{!} (failed @ %s:%d) ", __FILE__, __LINE__); \
printf(M); \
putchar('\n'); \
exit(EXIT_FAILURE); \
} while (0)
typedef enum log_level {
LOG_LEVEL_OFF = 1,
LOG_LEVEL_ERROR = 2,
+42 -75
View File
@@ -8,6 +8,8 @@
#include "Model.h"
#include <ctime>
#include <vector>
#include <stdarg.h>
#include <time.h>
@@ -371,12 +373,12 @@ Model::AddListener(const ModelListenerRef& listener)
PackageInfoRef
Model::PackageForName(const BString& name)
{
DepotList depots = Depots();
for (int32 d = 0; d < depots.CountItems(); d++) {
const DepotInfo& depot = depots.ItemAtFast(d);
int32 packageIndex = depot.PackageIndexByName(name);
std::vector<DepotInfoRef>::iterator it;
for (it = fDepots.begin(); it != fDepots.end(); it++) {
DepotInfoRef depotInfoRef = *it;
int32 packageIndex = depotInfoRef->PackageIndexByName(name);
if (packageIndex >= 0)
return depot.Packages().ItemAtFast(packageIndex);
return depotInfoRef->Packages().ItemAtFast(packageIndex);
}
return PackageInfoRef();
}
@@ -395,53 +397,63 @@ Model::MatchesFilter(const PackageInfoRef& package) const
}
bool
Model::AddDepot(const DepotInfo& depot)
void
Model::MergeOrAddDepot(const DepotInfoRef depot)
{
return fDepots.Add(depot);
BString depotName = depot->Name();
for(int32 i = 0; i < fDepots.size(); i++) {
if (fDepots[i]->Name() == depotName) {
DepotInfoRef ersatzDepot(new DepotInfo(*(fDepots[i].Get())), true);
ersatzDepot->SyncPackages(depot->Packages());
fDepots[i] = ersatzDepot;
return;
}
}
fDepots.push_back(depot);
}
bool
Model::HasDepot(const BString& name) const
{
return NULL != DepotForName(name);
return NULL != DepotForName(name).Get();
}
const DepotInfo*
const DepotInfoRef
Model::DepotForName(const BString& name) const
{
for (int32 i = fDepots.CountItems() - 1; i >= 0; i--) {
if (fDepots.ItemAtFast(i).Name() == name)
return &fDepots.ItemAtFast(i);
std::vector<DepotInfoRef>::const_iterator it;
for (it = fDepots.begin(); it != fDepots.end(); it++) {
DepotInfoRef aDepot = *it;
if (aDepot->Name() == name)
return aDepot;
}
return NULL;
return DepotInfoRef();
}
bool
Model::SyncDepot(const DepotInfo& depot)
int32
Model::CountDepots() const
{
for (int32 i = fDepots.CountItems() - 1; i >= 0; i--) {
const DepotInfo& existingDepot = fDepots.ItemAtFast(i);
if (existingDepot.Name() == depot.Name()) {
DepotInfo mergedDepot(existingDepot);
mergedDepot.SyncPackages(depot.Packages());
fDepots.Replace(i, mergedDepot);
return true;
}
}
return false;
return fDepots.size();
}
DepotInfoRef
Model::DepotAtIndex(int32 index) const
{
return fDepots[index];
}
bool
Model::HasAnyProminentPackages()
{
for (int32 i = fDepots.CountItems() - 1; i >= 0; i--) {
const DepotInfo& existingDepot = fDepots.ItemAtFast(i);
if (existingDepot.HasAnyProminentPackages())
std::vector<DepotInfoRef>::iterator it;
for (it = fDepots.begin(); it != fDepots.end(); it++) {
DepotInfoRef aDepot = *it;
if (aDepot->HasAnyProminentPackages())
return true;
}
return false;
@@ -451,7 +463,7 @@ Model::HasAnyProminentPackages()
void
Model::Clear()
{
fDepots.Clear();
fDepots.clear();
}
@@ -1036,51 +1048,6 @@ Model::_NotifyCategoryListChanged()
}
/*! This method will find the stored 'DepotInfo' that correlates to the
supplied 'identifier' and will invoke the mapper function in order
to get a replacement for the 'DepotInfo'. The 'identifier' holds
across mirrors.
*/
void
Model::ReplaceDepotByIdentifier(const BString& identifier,
DepotMapper* depotMapper, void* context)
{
for (int32 i = 0; i < fDepots.CountItems(); i++) {
DepotInfo depotInfo = fDepots.ItemAtFast(i);
if (identifier == depotInfo.URL()) {
BAutolock locker(&fLock);
fDepots.Replace(i, depotMapper->MapDepot(depotInfo, context));
}
}
}
void
Model::LogDepotsWithNoWebAppRepositoryCode() const
{
int32 i;
for (i = 0; i < fDepots.CountItems(); i++) {
const DepotInfo& depot = fDepots.ItemAt(i);
if (depot.WebAppRepositoryCode().Length() == 0) {
if (depot.URL().Length() > 0) {
HDINFO("depot [%s] (%s) correlates with no repository in the"
" the haiku depot server system", depot.Name().String(),
depot.URL().String());
}
else {
HDINFO("depot [%s] correlates with no repository in the"
" the haiku depot server system", depot.Name().String());
}
}
}
}
void
Model::_MaybeLogJsonRpcError(const BMessage &responsePayload,
const char *sourceDescription) const
+8 -20
View File
@@ -6,6 +6,8 @@
#ifndef MODEL_H
#define MODEL_H
#include <vector>
#include <Locker.h>
#include "AbstractProcess.h"
@@ -46,13 +48,6 @@ public:
};
class DepotMapper {
public:
virtual DepotInfo MapDepot(const DepotInfo& depot,
void* context) = 0;
};
class PackageConsumer {
public:
virtual bool ConsumePackage(
@@ -84,12 +79,11 @@ public:
bool MatchesFilter(
const PackageInfoRef& package) const;
bool AddDepot(const DepotInfo& depot);
void MergeOrAddDepot(const DepotInfoRef depot);
bool HasDepot(const BString& name) const;
const DepotList& Depots() const
{ return fDepots; }
const DepotInfo* DepotForName(const BString& name) const;
bool SyncDepot(const DepotInfo& depot);
int32 CountDepots() const;
DepotInfoRef DepotAtIndex(int32 index) const;
const DepotInfoRef DepotForName(const BString& name) const;
bool HasAnyProminentPackages();
void Clear();
@@ -150,19 +144,12 @@ public:
GetWebAppInterface() const
{ return fWebAppInterface; }
void ReplaceDepotByIdentifier(
const BString& identifier,
DepotMapper* depotMapper,
void* context);
status_t IconTarPath(BPath& path) const;
status_t DumpExportReferenceDataPath(BPath& path);
status_t DumpExportRepositoryDataPath(BPath& path);
status_t DumpExportPkgDataPath(BPath& path,
const BString& repositorySourceCode);
void LogDepotsWithNoWebAppRepositoryCode() const;
private:
void _AddCategory(const CategoryRef& category);
@@ -186,7 +173,8 @@ private:
private:
BLocker fLock;
DepotList fDepots;
std::vector<DepotInfoRef>
fDepots;
CategoryList fCategories;
+4 -4
View File
@@ -1,6 +1,7 @@
/*
* Copyright 2013, Stephan Aßmus <[email protected]>.
* Copyright 2013, Rene Gollent, <[email protected]>
* Copyright 2020, Andrew Lindesay <[email protected]>
*
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -42,11 +43,10 @@ PackageInfoRef
PackageAction::FindPackageByName(const BString& name)
{
Model* model = GetModel();
const DepotList& depots = model->Depots();
// TODO: optimize!
for (int32 i = 0; i < depots.CountItems(); i++) {
const DepotInfo& depot = depots.ItemAtFast(i);
const PackageList& packages = depot.Packages();
for (int32 i = 0; i < model->CountDepots(); i++) {
const DepotInfoRef depotInfoRef = model->DepotAtIndex(i);
const PackageList& packages = depotInfoRef->Packages();
for (int32 j = 0; j < packages.CountItems(); j++) {
PackageInfoRef info = packages.ItemAtFast(j);
if (info->Name() == name)
@@ -280,8 +280,7 @@ PackageIconTarRepository::_ToIconCacheKeySuffix(BitmapSize size)
case BITMAP_SIZE_ANY:
return "any";
default:
HDERROR("unsupported bitmap size");
exit(1);
HDFATAL("unsupported bitmap size");
break;
}
}
+2 -2
View File
@@ -410,7 +410,7 @@ typedef BReference<PackageInfo> PackageInfoRef;
typedef List<PackageInfoRef, false> PackageList;
class DepotInfo {
class DepotInfo : public BReferenceable {
public:
DepotInfo();
DepotInfo(const BString& name);
@@ -458,7 +458,7 @@ private:
};
typedef List<DepotInfo, false> DepotList;
typedef BReference<DepotInfo> DepotInfoRef;
#endif // PACKAGE_INFO_H
@@ -98,17 +98,21 @@ LocalPkgDataLoadProcess::RunInternal()
if (result != B_OK)
return result;
std::vector<DepotInfo> depots(repositoryNames.CountStrings());
std::vector<DepotInfoRef> depots(repositoryNames.CountStrings());
for (int32 i = 0; i < repositoryNames.CountStrings(); i++) {
const BString& repoName = repositoryNames.StringAt(i);
DepotInfo depotInfo = DepotInfo(repoName);
DepotInfoRef depotInfoRef = DepotInfoRef(
new(std::nothrow) DepotInfo(repoName), true);
if (depotInfoRef.Get() == NULL)
HDFATAL("unable to create new depot info - memory exhaustion");
BRepositoryConfig repoConfig;
status_t getRepositoryConfigStatus = roster.GetRepositoryConfig(
repoName, &repoConfig);
if (getRepositoryConfigStatus == B_OK) {
depotInfo.SetURL(repoConfig.Identifier());
depotInfoRef->SetURL(repoConfig.Identifier());
HDDEBUG("[%s] local repository [%s] identifier; [%s]",
Name(), repoName.String(), repoConfig.Identifier().String());
} else {
@@ -117,7 +121,7 @@ LocalPkgDataLoadProcess::RunInternal()
repoName.String(), strerror(getRepositoryConfigStatus));
}
depots[i] = depotInfo;
depots[i] = depotInfoRef;
}
PackageManager manager(B_PACKAGE_INSTALLATION_LOCATION_HOME);
@@ -210,11 +214,11 @@ LocalPkgDataLoadProcess::RunInternal()
if (remoteRepository != NULL) {
std::vector<DepotInfo>::iterator it;
std::vector<DepotInfoRef>::iterator it;
for (it = depots.begin(); it != depots.end(); it++) {
if (RepositoryUrlUtils::EqualsNormalized(
it->URL(), remoteRepository->Config().Identifier())) {
(*it)->URL(), remoteRepository->Config().Identifier())) {
break;
}
}
@@ -223,7 +227,7 @@ LocalPkgDataLoadProcess::RunInternal()
HDDEBUG("pkg [%s] repository [%s] not recognized --> ignored",
modelInfo->Name().String(), repositoryName.String());
} else {
it->AddPackage(modelInfo);
(*it)->AddPackage(modelInfo);
HDTRACE("pkg [%s] assigned to [%s]",
modelInfo->Name().String(), repositoryName.String());
}
@@ -264,22 +268,23 @@ LocalPkgDataLoadProcess::RunInternal()
if (!foundPackages.empty()) {
BString repoName = B_TRANSLATE("Local");
depots.push_back(DepotInfo(repoName));
DepotInfoRef depotInfoRef(new(std::nothrow) DepotInfo(repoName), true);
if (depotInfoRef.Get() == NULL)
HDFATAL("unable to create a new depot info - memory exhaustion");
depots.push_back(depotInfoRef);
for (PackageInfoMap::iterator it = foundPackages.begin();
it != foundPackages.end(); ++it) {
depots.back().AddPackage(it->second);
depotInfoRef->AddPackage(it->second);
}
}
{
std::vector<DepotInfo>::iterator it;
std::vector<DepotInfoRef>::iterator it;
for (it = depots.begin(); it != depots.end(); it++) {
if (fModel->HasDepot(it->Name()))
fModel->SyncDepot(*it);
else
fModel->AddDepot(*it);
fModel->MergeOrAddDepot(*it);
}
}
@@ -236,21 +236,19 @@ ServerIconExportUpdateProcess::UrlPathComponent()
void
ServerIconExportUpdateProcess::_NotifyPackagesWithIconsInDepots() const
{
const DepotList& depots = fModel->Depots();
for (int32 d = 0; d < depots.CountItems(); d++) {
const DepotInfo& depot = depots.ItemAtFast(d);
_NotifyPackagesWithIconsInDepot(depot);
for (int32 d = 0; d < fModel->CountDepots(); d++) {
_NotifyPackagesWithIconsInDepot(fModel->DepotAtIndex(d));
}
}
void
ServerIconExportUpdateProcess::_NotifyPackagesWithIconsInDepot(
const DepotInfo& depot) const
const DepotInfoRef& depot) const
{
PackageIconRepository& packageIconRepository
= fModel->GetPackageIconRepository();
const PackageList& packages = depot.Packages();
const PackageList& packages = depot->Packages();
for (int32 p = 0; p < packages.CountItems(); p++) {
AutoLocker<BLocker> locker(fModel->Lock());
const PackageInfoRef& packageInfoRef = packages.ItemAtFast(p);
@@ -45,7 +45,7 @@ protected:
private:
void _NotifyPackagesWithIconsInDepots() const;
void _NotifyPackagesWithIconsInDepot(
const DepotInfo& depotInfo) const;
const DepotInfoRef& depotInfo) const;
private:
Model* fModel;
@@ -2,8 +2,6 @@
* Copyright 2017-2020, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "ServerRepositoryDataUpdateProcess.h"
#include <stdio.h>
@@ -11,29 +9,24 @@
#include <time.h>
#include <AutoDeleter.h>
#include <AutoLocker.h>
#include <Autolock.h>
#include <Catalog.h>
#include <FileIO.h>
#include <Url.h>
#include "DumpExportRepository.h"
#include "DumpExportRepositoryJsonListener.h"
#include "DumpExportRepositorySource.h"
#include "PackageInfo.h"
#include "ServerSettings.h"
#include "StorageUtils.h"
#include "Logger.h"
#include "DumpExportRepository.h"
#include "DumpExportRepositorySource.h"
#include "DumpExportRepositoryJsonListener.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ServerRepositoryDataUpdateProcess"
struct repository_and_repository_source {
DumpExportRepository* repository;
DumpExportRepositorySource* repositorySource;
};
/*! This repository listener (not at the JSON level) is feeding in the
repositories as they are parsed and processing them. Processing
includes finding the matching depot record and coupling the data
@@ -41,28 +34,30 @@ struct repository_and_repository_source {
*/
class DepotMatchingRepositoryListener :
public DumpExportRepositoryListener, public DepotMapper {
public DumpExportRepositoryListener {
public:
DepotMatchingRepositoryListener(Model* model,
Stoppable* stoppable);
virtual ~DepotMatchingRepositoryListener();
virtual DepotInfo MapDepot(const DepotInfo& depot, void *context);
virtual bool Handle(DumpExportRepository* item);
void Handle(repository_and_repository_source& pair);
void Handle(DumpExportRepository* repository,
DumpExportRepositorySource*
repositorySource);
void Handle(const BString& identifier,
repository_and_repository_source& pair);
DumpExportRepository* repository,
DumpExportRepositorySource*
repositorySource);
virtual void Complete();
private:
void NormalizeUrl(BUrl& url) const;
bool IsUnassociatedDepotByUrl(
const DepotInfo& depotInfo,
const BString& urlStr) const;
int32 IndexOfUnassociatedDepotByUrl(
const BString& url) const;
void _SetupRepositoryData(
DepotInfoRef& depot,
DumpExportRepository* repository,
DumpExportRepositorySource*
repositorySource);
private:
Model* fModel;
Stoppable* fStoppable;
};
@@ -82,68 +77,66 @@ DepotMatchingRepositoryListener::~DepotMatchingRepositoryListener()
}
/*! This is invoked as a result of logic in 'Handle(..)' that requests that the
model call this method with the requested DepotInfo instance.
*/
DepotInfo
DepotMatchingRepositoryListener::MapDepot(const DepotInfo& depot, void *context)
void
DepotMatchingRepositoryListener::_SetupRepositoryData(DepotInfoRef& depot,
DumpExportRepository* repository,
DumpExportRepositorySource* repositorySource)
{
repository_and_repository_source* repositoryAndRepositorySource =
(repository_and_repository_source*) context;
BString* repositoryCode =
repositoryAndRepositorySource->repository->Code();
BString* repositorySourceCode =
repositoryAndRepositorySource->repositorySource->Code();
BString* repositoryCode = repository->Code();
BString* repositorySourceCode = repositorySource->Code();
DepotInfo modifiedDepotInfo(depot);
modifiedDepotInfo.SetWebAppRepositoryCode(BString(*repositoryCode));
modifiedDepotInfo.SetWebAppRepositorySourceCode(
BString(*repositorySourceCode));
depot->SetWebAppRepositoryCode(*repositoryCode);
depot->SetWebAppRepositorySourceCode(*repositorySourceCode);
if (Logger::IsDebugEnabled()) {
HDDEBUG("[DepotMatchingRepositoryListener] associated depot [%s] (%s) "
"with server repository source [%s] (%s)",
modifiedDepotInfo.Name().String(),
modifiedDepotInfo.URL().String(),
depot->Name().String(),
depot->URL().String(),
repositorySourceCode->String(),
repositoryAndRepositorySource
->repositorySource->Identifier()->String());
repositorySource->Identifier()->String());
} else {
HDINFO("[DepotMatchingRepositoryListener] associated depot [%s] with "
"server repository source [%s]",
modifiedDepotInfo.Name().String(),
depot->Name().String(),
repositorySourceCode->String());
}
return modifiedDepotInfo;
}
void
DepotMatchingRepositoryListener::Handle(const BString& identifier,
repository_and_repository_source& pair)
DumpExportRepository* repository,
DumpExportRepositorySource* repositorySource)
{
if (!identifier.IsEmpty()) {
fModel->ReplaceDepotByIdentifier(identifier, this, &pair);
AutoLocker<BLocker> locker(fModel->Lock());
for (int32 i = 0; i < fModel->CountDepots(); i++) {
DepotInfoRef depot = fModel->DepotAtIndex(i);
BString depotUrl = depot->URL();
if (identifier == depotUrl)
_SetupRepositoryData(depot, repository, repositorySource);
}
}
}
void
DepotMatchingRepositoryListener::Handle(repository_and_repository_source& pair)
DepotMatchingRepositoryListener::Handle(DumpExportRepository* repository,
DumpExportRepositorySource* repositorySource)
{
if (!pair.repositorySource->IdentifierIsNull())
Handle(*(pair.repositorySource->Identifier()), pair);
if (!repositorySource->IdentifierIsNull())
Handle(*(repositorySource->Identifier()), repository, repositorySource);
// there may be additional identifiers for the remote repository and
// these should also be taken into consideration.
for(int32 i = 0;
i < pair.repositorySource->CountExtraIdentifiers();
i < repositorySource->CountExtraIdentifiers();
i++)
{
Handle(*(pair.repositorySource->ExtraIdentifiersItemAt(i)), pair);
Handle(*(repositorySource->ExtraIdentifiersItemAt(i)), repository,
repositorySource);
}
}
@@ -152,15 +145,8 @@ bool
DepotMatchingRepositoryListener::Handle(DumpExportRepository* repository)
{
int32 i;
for (i = 0; i < repository->CountRepositorySources(); i++) {
repository_and_repository_source repositoryAndRepositorySource;
repositoryAndRepositorySource.repository = repository;
repositoryAndRepositorySource.repositorySource =
repository->RepositorySourcesItemAt(i);
Handle(repositoryAndRepositorySource);
}
for (i = 0; i < repository->CountRepositorySources(); i++)
Handle(repository, repository->RepositorySourcesItemAt(i));
return !fStoppable->WasStopped();
}
+1 -1
View File
@@ -490,7 +490,7 @@ App::_CheckPackageDaemonRuns()
alert->SetShortcut(0, B_ESCAPE);
if (alert->Go() == 0)
exit(1);
HDFATAL("unable to start without the package daemon running");
if (!_LaunchPackageDaemon())
break;
+30 -14
View File
@@ -826,13 +826,11 @@ MainWindow::_AdoptModel()
if (fSinglePackageMode)
return;
fModel.Lock()->Lock();
const DepotList& depots = fModel.Depots();
fModel.Lock()->Unlock();
for (int32 d = 0; d < depots.CountItems(); d++) {
const DepotInfo& depot = depots.ItemAtFast(d);
const PackageList& packages = depot.Packages();
std::vector<DepotInfoRef> depots = _CreateSnapshotOfDepots();
std::vector<DepotInfoRef>::iterator it;
for (it = depots.begin(); it != depots.end(); it++) {
DepotInfoRef depotInfoRef = *it;
const PackageList& packages = depotInfoRef->Packages();
for (int32 p = 0; p < packages.CountItems(); p++)
_AddRemovePackageFromLists(packages.ItemAtFast(p));
}
@@ -1147,17 +1145,23 @@ MainWindow::_UpdateAvailableRepositories()
fRepositoryMenu->AddItem(new BSeparatorItem());
bool foundSelectedDepot = false;
const DepotList& depots = fModel.Depots();
for (int i = 0; i < depots.CountItems(); i++) {
const DepotInfo& depot = depots.ItemAtFast(i);
std::vector<DepotInfoRef> depots = _CreateSnapshotOfDepots();
std::vector<DepotInfoRef>::iterator it;
if (depot.Name().Length() != 0) {
for (it = depots.begin(); it != depots.end(); it++) {
DepotInfoRef depot = *it;
if (depot->Name().Length() != 0) {
BMessage* message = new BMessage(MSG_DEPOT_SELECTED);
message->AddString("name", depot.Name());
BMenuItem* item = new BMenuItem(depot.Name(), message);
message->AddString("name", depot->Name());
BMenuItem* item = new(std::nothrow) BMenuItem(depot->Name(), message);
if (item == NULL)
HDFATAL("memory exhaustion");
fRepositoryMenu->AddItem(item);
if (depot.Name() == fModel.Depot()) {
if (depot->Name() == fModel.Depot()) {
item->SetMarked(true);
foundSelectedDepot = true;
}
@@ -1459,4 +1463,16 @@ MainWindow::_HandleChangePackageListViewMode()
BAutolock locker(fModel.Lock());
fModel.SetPackageListViewMode(tabMode);
}
}
std::vector<DepotInfoRef>
MainWindow::_CreateSnapshotOfDepots()
{
std::vector<DepotInfoRef> result;
BAutolock locker(fModel.Lock());
int32 countDepots = fModel.CountDepots();
for(int32 i = 0; i < countDepots; i++)
result.push_back(fModel.DepotAtIndex(i));
return result;
}
+3
View File
@@ -71,6 +71,9 @@ private:
virtual Model* GetModel();
private:
std::vector<DepotInfoRef>
_CreateSnapshotOfDepots();
void _AddProcessCoordinator(
ProcessCoordinator* item);
void _StopProcessCoordinators();
+4 -4
View File
@@ -13,6 +13,8 @@
#include <LocaleRoster.h>
#include <StringFormat.h>
#include "Logger.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "LocaleUtils"
@@ -38,10 +40,8 @@ LocaleUtils::GetCollator(BCollator* collator)
{
const BLocale* locale = BLocaleRoster::Default()->GetDefaultLocale();
if (B_OK != locale->GetCollator(collator)) {
debugger("unable to get the locale's collator");
exit(EXIT_FAILURE);
}
if (locale->GetCollator(collator) != B_OK)
HDFATAL("unable to get the locale's collator");
}