From 3f8b4604befd37cc3dc73a2d1321cf28bc1a74b4 Mon Sep 17 00:00:00 2001
From: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Date: Sat, 4 May 2024 13:39:04 +0200
Subject: [PATCH] core: convert taxonomy.c to C++

Since the taxonomy is now a real C++ struct with constructor
and destructor, dive_site has to be converted to C++ as well.

A bit hairy for now, but will ultimately be distinctly simpler.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
---
 Subsurface-mobile.pro                        |   2 +-
 backend-shared/exportfuncs.cpp               |   6 +-
 commands/command_divelist.cpp                |   4 +-
 commands/command_divelist.h                  |   4 +-
 commands/command_divesite.cpp                |  27 ++---
 commands/command_divesite.h                  |  14 +--
 commands/command_edit.cpp                    |   2 +-
 commands/command_edit.h                      |   4 +-
 commands/command_pictures.cpp                |   7 +-
 commands/command_pictures.h                  |   2 +-
 core/CMakeLists.txt                          |   2 +-
 core/dive.cpp                                |   4 +-
 core/dive.h                                  |   8 +-
 core/divelist.cpp                            |   4 +-
 core/divesite-helper.cpp                     |  13 +--
 core/divesite.cpp                            | 101 ++++++++---------
 core/divesite.h                              |  29 ++---
 core/divesitehelpers.cpp                     |  14 +--
 core/fulltext.cpp                            |   6 +-
 core/load-git.cpp                            |   4 +-
 core/owning_ptrs.h                           |   5 -
 core/parse-xml.cpp                           |   8 +-
 core/parse.cpp                               |  12 +-
 core/parse.h                                 |   3 +-
 core/pref.h                                  |   6 +-
 core/save-git.cpp                            |   9 +-
 core/save-xml.cpp                            |  30 ++---
 core/string-format.cpp                       |   8 +-
 core/taxonomy.c                              | 111 -------------------
 core/taxonomy.cpp                            |  59 ++++++++++
 core/taxonomy.h                              |  34 +++---
 core/uploadDiveLogsDE.cpp                    |  15 +--
 desktop-widgets/divesiteimportdialog.cpp     |   2 +-
 desktop-widgets/locationinformation.cpp      |  16 ++-
 desktop-widgets/modeldelegates.cpp           |   6 +-
 desktop-widgets/tab-widgets/TabDiveNotes.cpp |   2 +-
 mobile-widgets/qmlmanager.cpp                |   6 +-
 qt-models/divesiteimportmodel.cpp            |   2 +-
 qt-models/divetripmodel.cpp                  |   4 +-
 39 files changed, 259 insertions(+), 336 deletions(-)
 delete mode 100644 core/taxonomy.c
 create mode 100644 core/taxonomy.cpp

diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro
index 7f416b25a..1eae8080b 100644
--- a/Subsurface-mobile.pro
+++ b/Subsurface-mobile.pro
@@ -95,7 +95,7 @@ SOURCES += subsurface-mobile-main.cpp \
 	core/string-format.cpp \
 	core/strtod.cpp \
 	core/tag.cpp \
-	core/taxonomy.c \
+	core/taxonomy.cpp \
 	core/time.cpp \
 	core/trip.c \
 	core/units.cpp \
diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp
index 7bc545ae5..50cf45e3f 100644
--- a/backend-shared/exportfuncs.cpp
+++ b/backend-shared/exportfuncs.cpp
@@ -145,10 +145,10 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall
 		struct tm tm;
 		utc_mkdate(dive->when, &tm);
 
-		const char *country = NULL;
+		std::string country;
 		dive_site *site = dive->dive_site;
 		if (site)
-			country = taxonomy_get_country(&site->taxonomy);
+			country = taxonomy_get_country(site->taxonomy);
 		pressure_t delta_p = {.mbar = 0};
 
 		QString star = "*";
@@ -180,7 +180,7 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall
 		site ? put_format(&buf, "\\def\\%sgpslat{%f}\n", ssrf, site->location.lat.udeg / 1000000.0) : put_format(&buf, "\\def\\%sgpslat{}\n", ssrf);
 		site ? put_format(&buf, "\\def\\%sgpslon{%f}\n", ssrf, site->location.lon.udeg / 1000000.0) : put_format(&buf, "\\def\\gpslon{}\n");
 		put_format(&buf, "\\def\\%scomputer{%s}\n", ssrf, dive->dc.model);
-		put_format(&buf, "\\def\\%scountry{%s}\n", ssrf, country ?: "");
+		put_format(&buf, "\\def\\%scountry{%s}\n", ssrf, country.c_str());
 		put_format(&buf, "\\def\\%stime{%u:%02u}\n", ssrf, FRACTION_TUPLE(dive->duration.seconds, 60));
 
 		put_format(&buf, "\n%% Dive Profile Details:\n");
diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp
index 6ca13f5f9..22a8706e9 100644
--- a/commands/command_divelist.cpp
+++ b/commands/command_divelist.cpp
@@ -125,7 +125,7 @@ DivesAndTripsToAdd DiveListBase::removeDives(DivesAndSitesToRemove &divesAndSite
 {
 	std::vector<DiveToAdd> divesToAdd;
 	std::vector<OwningTripPtr> tripsToAdd;
-	std::vector<OwningDiveSitePtr> sitesToAdd;
+	std::vector<std::unique_ptr<dive_site>> sitesToAdd;
 	divesToAdd.reserve(divesAndSitesToDelete.dives.size());
 	sitesToAdd.reserve(divesAndSitesToDelete.sites.size());
 
@@ -216,7 +216,7 @@ DivesAndSitesToRemove DiveListBase::addDives(DivesAndTripsToAdd &toAdd)
 	toAdd.trips.clear();
 
 	// Finally, add any necessary dive sites
-	for (OwningDiveSitePtr &ds: toAdd.sites) {
+	for (std::unique_ptr<dive_site> &ds: toAdd.sites) {
 		sites.push_back(ds.get());
 		int idx = register_dive_site(ds.release()); // Return ownership to backend
 		emit diveListNotifier.diveSiteAdded(sites.back(), idx);
diff --git a/commands/command_divelist.h b/commands/command_divelist.h
index 43713fd58..bb47af725 100644
--- a/commands/command_divelist.h
+++ b/commands/command_divelist.h
@@ -24,7 +24,7 @@ struct DiveToAdd {
 struct DivesAndTripsToAdd {
 	std::vector<DiveToAdd> dives;
 	std::vector<OwningTripPtr> trips;
-	std::vector<OwningDiveSitePtr> sites;
+	std::vector<std::unique_ptr<dive_site>> sites;
 };
 
 // Dives and sites that have to be removed for a command
@@ -111,7 +111,7 @@ private:
 	struct device_table	devicesToAddAndRemove;
 
 	// For redo
-	std::vector<OwningDiveSitePtr>	sitesToAdd;
+	std::vector<std::unique_ptr<dive_site>>	sitesToAdd;
 	std::vector<std::pair<std::string,FilterData>>
 					filterPresetsToAdd;
 
diff --git a/commands/command_divesite.cpp b/commands/command_divesite.cpp
index b89bb0b86..76f88a175 100644
--- a/commands/command_divesite.cpp
+++ b/commands/command_divesite.cpp
@@ -15,13 +15,13 @@ namespace Command {
 
 // Add a set of dive sites to the core. The dives that were associated with
 // that dive site will be restored to that dive site.
-static std::vector<dive_site *> addDiveSites(std::vector<OwningDiveSitePtr> &sites)
+static std::vector<dive_site *> addDiveSites(std::vector<std::unique_ptr<dive_site>> &sites)
 {
 	std::vector<dive_site *> res;
 	QVector<dive *> changedDives;
 	res.reserve(sites.size());
 
-	for (OwningDiveSitePtr &ds: sites) {
+	for (std::unique_ptr<dive_site> &ds: sites) {
 		// Readd the dives that belonged to this site
 		for (int i = 0; i < ds->dives.nr; ++i) {
 			// TODO: send dive site changed signal
@@ -47,9 +47,9 @@ static std::vector<dive_site *> addDiveSites(std::vector<OwningDiveSitePtr> &sit
 // Remove a set of dive sites. Get owning pointers to them. The dives are set to
 // being at no dive site, but the dive site will retain a list of dives, so
 // that the dives can be readded to the site on undo.
-static std::vector<OwningDiveSitePtr> removeDiveSites(std::vector<dive_site *> &sites)
+static std::vector<std::unique_ptr<dive_site>> removeDiveSites(std::vector<dive_site *> &sites)
 {
-	std::vector<OwningDiveSitePtr> res;
+	std::vector<std::unique_ptr<dive_site>> res;
 	QVector<dive *> changedDives;
 	res.reserve(sites.size());
 
@@ -77,7 +77,7 @@ static std::vector<OwningDiveSitePtr> removeDiveSites(std::vector<dive_site *> &
 AddDiveSite::AddDiveSite(const QString &name)
 {
 	setText(Command::Base::tr("add dive site"));
-	sitesToAdd.emplace_back(alloc_dive_site());
+	sitesToAdd.push_back(std::make_unique<dive_site>());
 	sitesToAdd.back()->name = copy_qstring(name);
 }
 
@@ -107,7 +107,7 @@ ImportDiveSites::ImportDiveSites(struct dive_site_table *sites, const QString &s
 		// the same name. We might want to be smarter here and merge dive site data, etc.
 		struct dive_site *old_ds = get_same_dive_site(new_ds);
 		if (old_ds) {
-			free_dive_site(new_ds);
+			delete new_ds;
 			continue;
 		}
 		sitesToAdd.emplace_back(new_ds);
@@ -256,20 +256,20 @@ void EditDiveSiteNotes::undo()
 }
 
 EditDiveSiteCountry::EditDiveSiteCountry(dive_site *dsIn, const QString &country) : ds(dsIn),
-	value(country)
+	value(country.toStdString())
 {
 	setText(Command::Base::tr("Edit dive site country"));
 }
 
 bool EditDiveSiteCountry::workToBeDone()
 {
-	return !same_string(qPrintable(value), taxonomy_get_country(&ds->taxonomy));
+	return value == taxonomy_get_country(ds->taxonomy);
 }
 
 void EditDiveSiteCountry::redo()
 {
-	QString old = taxonomy_get_country(&ds->taxonomy);
-	taxonomy_set_country(&ds->taxonomy, qPrintable(value), taxonomy_origin::GEOMANUAL);
+	std::string old = taxonomy_get_country(ds->taxonomy);
+	taxonomy_set_country(ds->taxonomy, value, taxonomy_origin::GEOMANUAL);
 	value = old;
 	emit diveListNotifier.diveSiteChanged(ds, LocationInformationModel::TAXONOMY); // Inform frontend of changed dive site.
 }
@@ -310,14 +310,11 @@ void EditDiveSiteLocation::undo()
 EditDiveSiteTaxonomy::EditDiveSiteTaxonomy(dive_site *dsIn, taxonomy_data &taxonomy) : ds(dsIn),
 	value(taxonomy)
 {
-	// We did a dumb copy. Erase the source to remove double references to strings.
-	memset(&taxonomy, 0, sizeof(taxonomy));
 	setText(Command::Base::tr("Edit dive site taxonomy"));
 }
 
 EditDiveSiteTaxonomy::~EditDiveSiteTaxonomy()
 {
-	free_taxonomy(&value);
 }
 
 bool EditDiveSiteTaxonomy::workToBeDone()
@@ -364,7 +361,7 @@ void MergeDiveSites::redo()
 	// The dives of the above dive sites were reset to no dive sites.
 	// Add them to the merged-into dive site. Thankfully, we remember
 	// the dives in the sitesToAdd vector.
-	for (const OwningDiveSitePtr &site: sitesToAdd) {
+	for (const std::unique_ptr<dive_site> &site: sitesToAdd) {
 		for (int i = 0; i < site->dives.nr; ++i) {
 			add_dive_to_dive_site(site->dives.dives[i], ds);
 			divesChanged.push_back(site->dives.dives[i]);
@@ -380,7 +377,7 @@ void MergeDiveSites::undo()
 
 	// Before readding the dive sites, unregister the corresponding dives so that they can be
 	// readded to their old dive sites.
-	for (const OwningDiveSitePtr &site: sitesToAdd) {
+	for (const std::unique_ptr<dive_site> &site: sitesToAdd) {
 		for (int i = 0; i < site->dives.nr; ++i) {
 			unregister_dive_from_dive_site(site->dives.dives[i]);
 			divesChanged.push_back(site->dives.dives[i]);
diff --git a/commands/command_divesite.h b/commands/command_divesite.h
index c81397410..b40ee2908 100644
--- a/commands/command_divesite.h
+++ b/commands/command_divesite.h
@@ -31,7 +31,7 @@ private:
 	std::vector<dive_site *> sitesToRemove;
 
 	// For redo
-	std::vector<OwningDiveSitePtr> sitesToAdd;
+	std::vector<std::unique_ptr<dive_site>> sitesToAdd;
 };
 
 class ImportDiveSites : public Base {
@@ -47,7 +47,7 @@ private:
 	std::vector<dive_site *> sitesToRemove;
 
 	// For redo
-	std::vector<OwningDiveSitePtr> sitesToAdd;
+	std::vector<std::unique_ptr<dive_site>> sitesToAdd;
 };
 
 class DeleteDiveSites : public Base {
@@ -62,7 +62,7 @@ private:
 	std::vector<dive_site *> sitesToRemove;
 
 	// For undo
-	std::vector<OwningDiveSitePtr> sitesToAdd;
+	std::vector<std::unique_ptr<dive_site>> sitesToAdd;
 };
 
 class PurgeUnusedDiveSites : public Base {
@@ -77,7 +77,7 @@ private:
 	std::vector<dive_site *> sitesToRemove;
 
 	// For undo
-	std::vector<OwningDiveSitePtr> sitesToAdd;
+	std::vector<std::unique_ptr<dive_site>> sitesToAdd;
 };
 
 class EditDiveSiteName : public Base {
@@ -125,7 +125,7 @@ private:
 	void redo() override;
 
 	dive_site *ds;
-	QString value; // Value to be set
+	std::string value; // Value to be set
 };
 
 class EditDiveSiteLocation : public Base {
@@ -167,7 +167,7 @@ private:
 	std::vector<dive_site *> sitesToRemove;
 
 	// For undo
-	std::vector<OwningDiveSitePtr> sitesToAdd;
+	std::vector<std::unique_ptr<dive_site>> sitesToAdd;
 };
 
 class ApplyGPSFixes : public Base {
@@ -183,7 +183,7 @@ private:
 	std::vector<dive_site *> sitesToRemove;
 
 	// For redo
-	std::vector<OwningDiveSitePtr> sitesToAdd;
+	std::vector<std::unique_ptr<dive_site>> sitesToAdd;
 
 	// For redo and undo
 	struct SiteAndLocation {
diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp
index 5b28c05e3..47d05a03c 100644
--- a/commands/command_edit.cpp
+++ b/commands/command_edit.cpp
@@ -376,7 +376,7 @@ void EditDiveSite::redo()
 
 static struct dive_site *createDiveSite(const QString &name)
 {
-	struct dive_site *ds = alloc_dive_site();
+	struct dive_site *ds = new dive_site;
 	struct dive_site *old = current_dive ? current_dive->dive_site : nullptr;
 	if (old) {
 		copy_dive_site(old, ds);
diff --git a/commands/command_edit.h b/commands/command_edit.h
index 529313037..deab84252 100644
--- a/commands/command_edit.h
+++ b/commands/command_edit.h
@@ -208,7 +208,7 @@ public:
 // deriving from it and hooks into undo() and redo() to add / remove the dive site.
 class EditDiveSiteNew : public EditDiveSite {
 public:
-	OwningDiveSitePtr diveSiteToAdd;
+	std::unique_ptr<dive_site> diveSiteToAdd;
 	struct dive_site *diveSiteToRemove;
 	EditDiveSiteNew(const QString &newName, bool currentDiveOnly);
 	void undo() override;
@@ -470,7 +470,7 @@ private:
 	int changedFields;
 
 	dive_site *siteToRemove;
-	OwningDiveSitePtr siteToAdd;
+	std::unique_ptr<dive_site> siteToAdd;
 
 	dive_site *siteToEdit;
 	location_t dsLocation;
diff --git a/commands/command_pictures.cpp b/commands/command_pictures.cpp
index 98f09e3c8..6c5ef8ea1 100644
--- a/commands/command_pictures.cpp
+++ b/commands/command_pictures.cpp
@@ -171,9 +171,8 @@ AddPictures::AddPictures(const std::vector<PictureListForAddition> &pictures) :
 			if (!ds) {
 				// This dive doesn't yet have a dive site -> add a new dive site.
 				QString name = Command::Base::tr("unnamed dive site");
-				dive_site *ds = alloc_dive_site_with_gps(qPrintable(name), &it->location);
-				sitesToAdd.emplace_back(ds);
-				sitesToSet.push_back({ p.d, ds });
+				sitesToAdd.push_back(std::make_unique<dive_site>(qPrintable(name), &it->location));
+				sitesToSet.push_back({ p.d, sitesToAdd.back().get() });
 			} else if (!dive_site_has_gps_location(ds)) {
 				// This dive has a dive site, but without coordinates. Let's add them.
 				sitesToEdit.push_back({ ds, it->location });
@@ -228,7 +227,7 @@ void AddPictures::undo()
 void AddPictures::redo()
 {
 	// Add dive sites
-	for (OwningDiveSitePtr &siteToAdd: sitesToAdd) {
+	for (std::unique_ptr<dive_site> &siteToAdd: sitesToAdd) {
 		sitesToRemove.push_back(siteToAdd.get());
 		int idx = register_dive_site(siteToAdd.release()); // Return ownership to backend.
 		emit diveListNotifier.diveSiteAdded(sitesToRemove.back(), idx); // Inform frontend of new dive site.
diff --git a/commands/command_pictures.h b/commands/command_pictures.h
index 472a92565..e85cf0409 100644
--- a/commands/command_pictures.h
+++ b/commands/command_pictures.h
@@ -48,7 +48,7 @@ private:
 		location_t location;
 	};
 	std::vector<PictureListForAddition> picturesToAdd; // for redo
-	std::vector<OwningDiveSitePtr> sitesToAdd; //for redo
+	std::vector<std::unique_ptr<dive_site>> sitesToAdd; //for redo
 	std::vector<PictureListForDeletion> picturesToRemove; // for undo
 	std::vector<dive_site *> sitesToRemove; // for undo
 	std::vector<DiveSiteEntry> sitesToSet; // for redo and undo
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
index 4b0d672aa..9d0cab87f 100644
--- a/core/CMakeLists.txt
+++ b/core/CMakeLists.txt
@@ -179,7 +179,7 @@ set(SUBSURFACE_CORE_LIB_SRCS
 	subsurfacesysinfo.h
 	tag.cpp
 	tag.h
-	taxonomy.c
+	taxonomy.cpp
 	taxonomy.h
 	time.cpp
 	trip.c
diff --git a/core/dive.cpp b/core/dive.cpp
index 05dd1e4f1..47718bc03 100644
--- a/core/dive.cpp
+++ b/core/dive.cpp
@@ -3335,10 +3335,10 @@ extern "C" struct dive_site *get_dive_site_for_dive(const struct dive *dive)
 	return dive->dive_site;
 }
 
-extern "C" const char *get_dive_country(const struct dive *dive)
+std::string get_dive_country(const struct dive *dive)
 {
 	struct dive_site *ds = dive->dive_site;
-	return ds ? taxonomy_get_country(&ds->taxonomy) : NULL;
+	return ds ? taxonomy_get_country(ds->taxonomy) : std::string();
 }
 
 extern "C" const char *get_dive_location(const struct dive *dive)
diff --git a/core/dive.h b/core/dive.h
index e85f19bed..ec326e40b 100644
--- a/core/dive.h
+++ b/core/dive.h
@@ -13,6 +13,7 @@
 #include <stdlib.h>
 
 #ifdef __cplusplus
+#include <string>
 extern "C" {
 #endif
 
@@ -114,7 +115,11 @@ extern depth_t gas_mnd(struct gasmix mix, depth_t end, const struct dive *dive,
 extern struct dive *get_dive(int nr);
 extern struct dive *get_dive_from_table(int nr, const struct dive_table *dt);
 extern struct dive_site *get_dive_site_for_dive(const struct dive *dive);
-extern const char *get_dive_country(const struct dive *dive);
+#ifdef __cplusplus
+} // TODO: remove
+extern std::string get_dive_country(const struct dive *dive);
+extern "C" {
+#endif
 extern const char *get_dive_location(const struct dive *dive);
 extern unsigned int number_of_computers(const struct dive *dive);
 extern struct divecomputer *get_dive_dc(struct dive *dive, int nr);
@@ -227,7 +232,6 @@ extern void update_setpoint_events(const struct dive *dive, struct divecomputer
  * QVariants and through QML.
  */
 #include <QObject>
-#include <string>
 Q_DECLARE_METATYPE(struct dive *);
 
 extern std::string existing_filename;
diff --git a/core/divelist.cpp b/core/divelist.cpp
index bdd4cac64..ab694b997 100644
--- a/core/divelist.cpp
+++ b/core/divelist.cpp
@@ -1144,7 +1144,7 @@ void process_imported_dives(struct divelog *import_log, int flags,
 
 		if (j == import_log->dives->nr) {
 			/* Dive site not even used - free it and go to next. */
-			free_dive_site(new_ds);
+			delete new_ds;
 			continue;
 		}
 
@@ -1159,7 +1159,7 @@ void process_imported_dives(struct divelog *import_log, int flags,
 			if (import_log->dives->dives[j]->dive_site == new_ds)
 				import_log->dives->dives[j]->dive_site = old_ds;
 		}
-		free_dive_site(new_ds);
+		delete new_ds;
 	}
 	import_log->sites->nr = 0; /* All dive sites were consumed */
 
diff --git a/core/divesite-helper.cpp b/core/divesite-helper.cpp
index bed0297ce..d9c06e9c0 100644
--- a/core/divesite-helper.cpp
+++ b/core/divesite-helper.cpp
@@ -3,11 +3,11 @@
 #include "pref.h"
 #include "gettextfromc.h"
 
-QString constructLocationTags(struct taxonomy_data *taxonomy, bool for_maintab)
+QString constructLocationTags(taxonomy_data &taxonomy, bool for_maintab)
 {
 	QString locationTag;
 
-	if (!taxonomy->nr)
+	if (taxonomy.empty())
 		return locationTag;
 
 	/* Check if the user set any of the 3 geocoding categories */
@@ -33,11 +33,10 @@ QString constructLocationTags(struct taxonomy_data *taxonomy, bool for_maintab)
 	for (int i = 0; i < 3; i++) {
 		if (prefs.geocoding.category[i] == TC_NONE)
 			continue;
-		for (int j = 0; j < taxonomy->nr; j++) {
-			if (taxonomy->category[j].category == prefs.geocoding.category[i]) {
-				QString tag = taxonomy->category[j].value;
-				if (!tag.isEmpty()) {
-					locationTag += connector + tag;
+		for (auto const &t: taxonomy) {
+			if (t.category == prefs.geocoding.category[i]) {
+				if (!t.value.empty()) {
+					locationTag += connector + QString::fromStdString(t.value);
 					connector = " / ";
 				}
 				break;
diff --git a/core/divesite.cpp b/core/divesite.cpp
index 5c23b2f22..e2830991b 100644
--- a/core/divesite.cpp
+++ b/core/divesite.cpp
@@ -12,7 +12,7 @@
 
 #include <math.h>
 
-extern "C" int get_divesite_idx(const struct dive_site *ds, struct dive_site_table *ds_table)
+int get_divesite_idx(const struct dive_site *ds, struct dive_site_table *ds_table)
 {
 	int i;
 	const struct dive_site *d;
@@ -26,7 +26,7 @@ extern "C" int get_divesite_idx(const struct dive_site *ds, struct dive_site_tab
 }
 
 // TODO: keep table sorted by UUID and do a binary search?
-extern "C" struct dive_site *get_dive_site_by_uuid(uint32_t uuid, struct dive_site_table *ds_table)
+struct dive_site *get_dive_site_by_uuid(uint32_t uuid, struct dive_site_table *ds_table)
 {
 	int i;
 	struct dive_site *ds;
@@ -37,7 +37,7 @@ extern "C" struct dive_site *get_dive_site_by_uuid(uint32_t uuid, struct dive_si
 }
 
 /* there could be multiple sites of the same name - return the first one */
-extern "C" struct dive_site *get_dive_site_by_name(const char *name, struct dive_site_table *ds_table)
+struct dive_site *get_dive_site_by_name(const char *name, struct dive_site_table *ds_table)
 {
 	int i;
 	struct dive_site *ds;
@@ -49,7 +49,7 @@ extern "C" struct dive_site *get_dive_site_by_name(const char *name, struct dive
 }
 
 /* there could be multiple sites at the same GPS fix - return the first one */
-extern "C" struct dive_site *get_dive_site_by_gps(const location_t *loc, struct dive_site_table *ds_table)
+struct dive_site *get_dive_site_by_gps(const location_t *loc, struct dive_site_table *ds_table)
 {
 	int i;
 	struct dive_site *ds;
@@ -63,7 +63,7 @@ extern "C" struct dive_site *get_dive_site_by_gps(const location_t *loc, struct
 /* to avoid a bug where we have two dive sites with different name and the same GPS coordinates
  * and first get the gps coordinates (reading a V2 file) and happen to get back "the other" name,
  * this function allows us to verify if a very specific name/GPS combination already exists */
-extern "C" struct dive_site *get_dive_site_by_gps_and_name(const char *name, const location_t *loc, struct dive_site_table *ds_table)
+struct dive_site *get_dive_site_by_gps_and_name(const char *name, const location_t *loc, struct dive_site_table *ds_table)
 {
 	int i;
 	struct dive_site *ds;
@@ -75,7 +75,7 @@ extern "C" struct dive_site *get_dive_site_by_gps_and_name(const char *name, con
 }
 
 // Calculate the distance in meters between two coordinates.
-extern "C" unsigned int get_distance(const location_t *loc1, const location_t *loc2)
+unsigned int get_distance(const location_t *loc1, const location_t *loc2)
 {
 	double lat1_r = udeg_to_radians(loc1->lat.udeg);
 	double lat2_r = udeg_to_radians(loc2->lat.udeg);
@@ -93,7 +93,7 @@ extern "C" unsigned int get_distance(const location_t *loc1, const location_t *l
 }
 
 /* find the closest one, no more than distance meters away - if more than one at same distance, pick the first */
-extern "C" struct dive_site *get_dive_site_by_gps_proximity(const location_t *loc, int distance, struct dive_site_table *ds_table)
+struct dive_site *get_dive_site_by_gps_proximity(const location_t *loc, int distance, struct dive_site_table *ds_table)
 {
 	int i;
 	struct dive_site *ds, *res = NULL;
@@ -108,7 +108,7 @@ extern "C" struct dive_site *get_dive_site_by_gps_proximity(const location_t *lo
 	return res;
 }
 
-extern "C" int register_dive_site(struct dive_site *ds)
+int register_dive_site(struct dive_site *ds)
 {
 	return add_dive_site_to_table(ds, divelog.sites);
 }
@@ -123,6 +123,11 @@ static int site_less_than(const struct dive_site *a, const struct dive_site *b)
 	return compare_sites(a, b) < 0;
 }
 
+static void free_dive_site(struct dive_site *ds)
+{
+	delete ds;
+}
+
 static MAKE_GROW_TABLE(dive_site_table, struct dive_site *, dive_sites)
 static MAKE_GET_INSERTION_INDEX(dive_site_table, struct dive_site *, dive_sites, site_less_than)
 static MAKE_ADD_TO(dive_site_table, struct dive_site *, dive_sites)
@@ -133,7 +138,7 @@ static MAKE_REMOVE(dive_site_table, struct dive_site *, dive_site)
 MAKE_CLEAR_TABLE(dive_site_table, dive_sites, dive_site)
 MAKE_MOVE_TABLE(dive_site_table, dive_sites)
 
-extern "C" int add_dive_site_to_table(struct dive_site *ds, struct dive_site_table *ds_table)
+int add_dive_site_to_table(struct dive_site *ds, struct dive_site_table *ds_table)
 {
 	/* If the site doesn't yet have an UUID, create a new one.
 	 * Make this deterministic for testing. */
@@ -158,35 +163,35 @@ extern "C" int add_dive_site_to_table(struct dive_site *ds, struct dive_site_tab
 	return idx;
 }
 
-extern "C" struct dive_site *alloc_dive_site()
+dive_site::dive_site()
 {
-	return (struct dive_site *)calloc(1, sizeof(struct dive_site));
 }
 
-extern "C" struct dive_site *alloc_dive_site_with_name(const char *name)
+dive_site::dive_site(const char *name) : name(copy_string(name))
 {
-	struct dive_site *ds = alloc_dive_site();
-	ds->name = copy_string(name);
-	return ds;
 }
 
-extern "C" struct dive_site *alloc_dive_site_with_gps(const char *name, const location_t *loc)
+dive_site::dive_site(const char *name, const location_t *loc) : name(copy_string(name)), location(*loc)
 {
-	struct dive_site *ds = alloc_dive_site_with_name(name);
-	ds->location = *loc;
+}
 
-	return ds;
+dive_site::~dive_site()
+{
+	free(name);
+	free(notes);
+	free(description);
+	free(dives.dives);
 }
 
 /* when parsing, dive sites are identified by uuid */
-extern "C" struct dive_site *alloc_or_get_dive_site(uint32_t uuid, struct dive_site_table *ds_table)
+struct dive_site *alloc_or_get_dive_site(uint32_t uuid, struct dive_site_table *ds_table)
 {
 	struct dive_site *ds;
 
 	if (uuid && (ds = get_dive_site_by_uuid(uuid, ds_table)) != NULL)
 		return ds;
 
-	ds = alloc_dive_site();
+	ds = new dive_site;
 	ds->uuid = uuid;
 
 	add_dive_site_to_table(ds, ds_table);
@@ -194,12 +199,12 @@ extern "C" struct dive_site *alloc_or_get_dive_site(uint32_t uuid, struct dive_s
 	return ds;
 }
 
-extern "C" int nr_of_dives_at_dive_site(struct dive_site *ds)
+int nr_of_dives_at_dive_site(struct dive_site *ds)
 {
 	return ds->dives.nr;
 }
 
-extern "C" bool is_dive_site_selected(struct dive_site *ds)
+bool is_dive_site_selected(struct dive_site *ds)
 {
 	int i;
 
@@ -210,49 +215,37 @@ extern "C" bool is_dive_site_selected(struct dive_site *ds)
 	return false;
 }
 
-extern "C" void free_dive_site(struct dive_site *ds)
-{
-	if (ds) {
-		free(ds->name);
-		free(ds->notes);
-		free(ds->description);
-		free(ds->dives.dives);
-		free_taxonomy(&ds->taxonomy);
-		free(ds);
-	}
-}
-
-extern "C" int unregister_dive_site(struct dive_site *ds)
+int unregister_dive_site(struct dive_site *ds)
 {
 	return remove_dive_site(ds, divelog.sites);
 }
 
-extern "C" void delete_dive_site(struct dive_site *ds, struct dive_site_table *ds_table)
+void delete_dive_site(struct dive_site *ds, struct dive_site_table *ds_table)
 {
 	if (!ds)
 		return;
 	remove_dive_site(ds, ds_table);
-	free_dive_site(ds);
+	delete ds;
 }
 
 /* allocate a new site and add it to the table */
-extern "C" struct dive_site *create_dive_site(const char *name, struct dive_site_table *ds_table)
+struct dive_site *create_dive_site(const char *name, struct dive_site_table *ds_table)
 {
-	struct dive_site *ds = alloc_dive_site_with_name(name);
+	struct dive_site *ds = new dive_site(name);
 	add_dive_site_to_table(ds, ds_table);
 	return ds;
 }
 
 /* same as before, but with GPS data */
-extern "C" struct dive_site *create_dive_site_with_gps(const char *name, const location_t *loc, struct dive_site_table *ds_table)
+struct dive_site *create_dive_site_with_gps(const char *name, const location_t *loc, struct dive_site_table *ds_table)
 {
-	struct dive_site *ds = alloc_dive_site_with_gps(name, loc);
+	struct dive_site *ds = new dive_site(name, loc);
 	add_dive_site_to_table(ds, ds_table);
 	return ds;
 }
 
 /* if all fields are empty, the dive site is pointless */
-extern "C" bool dive_site_is_empty(struct dive_site *ds)
+bool dive_site_is_empty(struct dive_site *ds)
 {
 	return !ds ||
 	       (empty_string(ds->name) &&
@@ -261,7 +254,7 @@ extern "C" bool dive_site_is_empty(struct dive_site *ds)
 	       !has_location(&ds->location));
 }
 
-extern "C" void copy_dive_site(struct dive_site *orig, struct dive_site *copy)
+void copy_dive_site(struct dive_site *orig, struct dive_site *copy)
 {
 	free(copy->name);
 	free(copy->notes);
@@ -271,7 +264,7 @@ extern "C" void copy_dive_site(struct dive_site *orig, struct dive_site *copy)
 	copy->name = copy_string(orig->name);
 	copy->notes = copy_string(orig->notes);
 	copy->description = copy_string(orig->description);
-	copy_taxonomy(&orig->taxonomy, &copy->taxonomy);
+	copy->taxonomy = orig->taxonomy;
 }
 
 static void merge_string(char **a, char **b)
@@ -307,7 +300,7 @@ static bool same_dive_site(const struct dive_site *a, const struct dive_site *b)
 	    && same_string(a->notes, b->notes);
 }
 
-extern "C" struct dive_site *get_same_dive_site(const struct dive_site *site)
+struct dive_site *get_same_dive_site(const struct dive_site *site)
 {
 	int i;
 	struct dive_site *ds;
@@ -317,20 +310,18 @@ extern "C" struct dive_site *get_same_dive_site(const struct dive_site *site)
 	return NULL;
 }
 
-extern "C" void merge_dive_site(struct dive_site *a, struct dive_site *b)
+void merge_dive_site(struct dive_site *a, struct dive_site *b)
 {
 	if (!has_location(&a->location)) a->location = b->location;
 	merge_string(&a->name, &b->name);
 	merge_string(&a->notes, &b->notes);
 	merge_string(&a->description, &b->description);
 
-	if (!a->taxonomy.category) {
-		a->taxonomy = b->taxonomy;
-		memset(&b->taxonomy, 0, sizeof(b->taxonomy));
-	}
+	if (a->taxonomy.empty())
+		a->taxonomy = std::move(b->taxonomy);
 }
 
-extern "C" struct dive_site *find_or_create_dive_site_with_name(const char *name, struct dive_site_table *ds_table)
+struct dive_site *find_or_create_dive_site_with_name(const char *name, struct dive_site_table *ds_table)
 {
 	int i;
 	struct dive_site *ds;
@@ -343,7 +334,7 @@ extern "C" struct dive_site *find_or_create_dive_site_with_name(const char *name
 	return create_dive_site(name, ds_table);
 }
 
-extern "C" void purge_empty_dive_sites(struct dive_site_table *ds_table)
+void purge_empty_dive_sites(struct dive_site_table *ds_table)
 {
 	int i, j;
 	struct dive *d;
@@ -360,7 +351,7 @@ extern "C" void purge_empty_dive_sites(struct dive_site_table *ds_table)
 	}
 }
 
-extern "C" void add_dive_to_dive_site(struct dive *d, struct dive_site *ds)
+void add_dive_to_dive_site(struct dive *d, struct dive_site *ds)
 {
 	int idx;
 	if (!d) {
@@ -382,7 +373,7 @@ extern "C" void add_dive_to_dive_site(struct dive *d, struct dive_site *ds)
 	d->dive_site = ds;
 }
 
-extern "C" struct dive_site *unregister_dive_from_dive_site(struct dive *d)
+struct dive_site *unregister_dive_from_dive_site(struct dive *d)
 {
 	struct dive_site *ds = d->dive_site;
 	if (!ds)
diff --git a/core/divesite.h b/core/divesite.h
index 29002587f..237f60085 100644
--- a/core/divesite.h
+++ b/core/divesite.h
@@ -10,20 +10,20 @@
 #ifdef __cplusplus
 #include <QString>
 #include <QObject>
-extern "C" {
-#else
-#include <stdbool.h>
-#endif
 
 struct dive_site
 {
-	uint32_t uuid;
-	char *name;
-	struct dive_table dives;
-	location_t location;
-	char *description;
-	char *notes;
-	struct taxonomy_data taxonomy;
+	uint32_t uuid = 0;
+	char *name = nullptr;
+	struct dive_table dives = { 0, 0, nullptr };
+	location_t location = { { 9 }, { 0 } };
+	char *description = nullptr;
+	char *notes = nullptr;
+	taxonomy_data taxonomy;
+	dive_site();
+	dive_site(const char *name);
+	dive_site(const char *name, const location_t *loc);
+	~dive_site();
 };
 
 typedef struct dive_site_table {
@@ -50,11 +50,8 @@ void sort_dive_site_table(struct dive_site_table *ds_table);
 int add_dive_site_to_table(struct dive_site *ds, struct dive_site_table *ds_table);
 struct dive_site *alloc_or_get_dive_site(uint32_t uuid, struct dive_site_table *ds_table);
 struct dive_site *alloc_dive_site();
-struct dive_site *alloc_dive_site_with_name(const char *name);
-struct dive_site *alloc_dive_site_with_gps(const char *name, const location_t *loc);
 int nr_of_dives_at_dive_site(struct dive_site *ds);
 bool is_dive_site_selected(struct dive_site *ds);
-void free_dive_site(struct dive_site *ds);
 int unregister_dive_site(struct dive_site *ds);
 int register_dive_site(struct dive_site *ds);
 void delete_dive_site(struct dive_site *ds, struct dive_site_table *ds_table);
@@ -77,9 +74,7 @@ void move_dive_site_table(struct dive_site_table *src, struct dive_site_table *d
 void add_dive_to_dive_site(struct dive *d, struct dive_site *ds);
 struct dive_site *unregister_dive_from_dive_site(struct dive *d);
 
-#ifdef __cplusplus
-}
-QString constructLocationTags(struct taxonomy_data *taxonomy, bool for_maintab);
+QString constructLocationTags(taxonomy_data &taxonomy, bool for_maintab);
 
 /* Make pointer-to-dive_site a "Qt metatype" so that we can pass it through QVariants */
 Q_DECLARE_METATYPE(dive_site *);
diff --git a/core/divesitehelpers.cpp b/core/divesitehelpers.cpp
index aee24d9eb..e4fd4af4e 100644
--- a/core/divesitehelpers.cpp
+++ b/core/divesitehelpers.cpp
@@ -84,14 +84,14 @@ taxonomy_data reverseGeoLookup(degrees_t latitude, degrees_t longitude)
 
 	QString url;
 	QJsonObject obj;
-	taxonomy_data taxonomy = { 0, 0 };
+	taxonomy_data taxonomy;
 
 	// check the oceans API to figure out the body of water
 	url = geonamesOceanURL.arg(getUiLanguage().section(QRegularExpression("[-_ ]"), 0, 0)).arg(latitude.udeg / 1000000.0).arg(longitude.udeg / 1000000.0);
 	obj = doAsyncRESTGetRequest(url, 5000); // 5 secs. timeout
 	QVariantMap oceanName = obj.value("ocean").toVariant().toMap();
 	if (oceanName["name"].isValid())
-		taxonomy_set_category(&taxonomy, TC_OCEAN, qPrintable(oceanName["name"].toString()), taxonomy_origin::GEOCODED);
+		taxonomy_set_category(taxonomy, TC_OCEAN, oceanName["name"].toString().toStdString(), taxonomy_origin::GEOCODED);
 
 	// check the findNearbyPlaces API from geonames - that should give us country, state, city
 	url = geonamesNearbyPlaceNameURL.arg(getUiLanguage().section(QRegularExpression("[-_ ]"), 0, 0)).arg(latitude.udeg / 1000000.0).arg(longitude.udeg / 1000000.0);
@@ -110,16 +110,16 @@ taxonomy_data reverseGeoLookup(degrees_t latitude, degrees_t longitude)
 		for (int idx = TC_COUNTRY; idx < TC_NR_CATEGORIES; idx++) {
 			if (firstData[taxonomy_api_names[idx]].isValid()) {
 				QString value = firstData[taxonomy_api_names[idx]].toString();
-				taxonomy_set_category(&taxonomy, (taxonomy_category)idx, qPrintable(value), taxonomy_origin::GEOCODED);
+				taxonomy_set_category(taxonomy, (taxonomy_category)idx, value.toStdString(), taxonomy_origin::GEOCODED);
 			}
 		}
-		const char *l3 = taxonomy_get_value(&taxonomy, TC_ADMIN_L3);
-		const char *lt = taxonomy_get_value(&taxonomy, TC_LOCALNAME);
-		if (empty_string(l3) && !empty_string(lt)) {
+		std::string l3 = taxonomy_get_value(taxonomy, TC_ADMIN_L3);
+		std::string lt = taxonomy_get_value(taxonomy, TC_LOCALNAME);
+		if (!l3.empty() && !lt.empty()) {
 			// basically this means we did get a local name (what we call town), but just like most places
 			// we didn't get an adminName_3 - which in some regions is the actual city that town belongs to,
 			// then we copy the town into the city
-			taxonomy_set_category(&taxonomy, TC_ADMIN_L3, lt, taxonomy_origin::GEOCOPIED);
+			taxonomy_set_category(taxonomy, TC_ADMIN_L3, lt, taxonomy_origin::GEOCOPIED);
 		}
 	} else {
 		report_error("geonames.org did not provide reverse lookup information");
diff --git a/core/fulltext.cpp b/core/fulltext.cpp
index 8a9c956d7..101ac876f 100644
--- a/core/fulltext.cpp
+++ b/core/fulltext.cpp
@@ -141,9 +141,9 @@ static std::vector<QString> getWords(const dive *d)
 	// take the tokens from a cache.
 	if (d->dive_site) {
 		tokenize(d->dive_site->name, res);
-		const char *country = taxonomy_get_country(&d->dive_site->taxonomy);
-		if (country)
-			tokenize(country, res);
+		std::string country = taxonomy_get_country(d->dive_site->taxonomy);
+		if (!country.empty())
+			tokenize(country.c_str(), res);
 	}
 	// TODO: We should index trips separately!
 	if (d->divetrip)
diff --git a/core/load-git.cpp b/core/load-git.cpp
index ba25794e1..becae6f58 100644
--- a/core/load-git.cpp
+++ b/core/load-git.cpp
@@ -332,8 +332,8 @@ static void parse_site_geo(char *line, struct git_parser_state *state)
 	int origin;
 	int category;
 	sscanf(line, "cat %d origin %d \"", &category, &origin);
-	taxonomy_set_category(&state->active_site->taxonomy, (taxonomy_category)category,
-			      get_first_converted_string(state).c_str(), (taxonomy_origin)origin);
+	taxonomy_set_category(state->active_site->taxonomy, (taxonomy_category)category,
+			      get_first_converted_string(state), (taxonomy_origin)origin);
 }
 
 static std::string pop_cstring(struct git_parser_state *state, const char *err)
diff --git a/core/owning_ptrs.h b/core/owning_ptrs.h
index 6f591d144..3ba5dc0a5 100644
--- a/core/owning_ptrs.h
+++ b/core/owning_ptrs.h
@@ -16,7 +16,6 @@ struct event;
 
 extern "C" void free_dive(struct dive *);
 extern "C" void free_trip(struct dive_trip *);
-extern "C" void free_dive_site(struct dive_site *);
 
 // Classes used to automatically call the appropriate free_*() function for owning pointers that go out of scope.
 struct DiveDeleter {
@@ -25,9 +24,6 @@ struct DiveDeleter {
 struct TripDeleter {
 	void operator()(dive_trip *t) { free_trip(t); }
 };
-struct DiveSiteDeleter {
-	void operator()(dive_site *ds) { free_dive_site(ds); }
-};
 struct EventDeleter {
 	void operator()(event *ev) { free(ev); }
 };
@@ -35,7 +31,6 @@ struct EventDeleter {
 // Owning pointers to dive, dive_trip, dive_site and event objects.
 using OwningDivePtr = std::unique_ptr<dive, DiveDeleter>;
 using OwningTripPtr = std::unique_ptr<dive_trip, TripDeleter>;
-using OwningDiveSitePtr = std::unique_ptr<dive_site, DiveSiteDeleter>;
 using OwningEventPtr = std::unique_ptr<event, EventDeleter>;
 
 #endif
diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp
index 8988d9602..f25d4533c 100644
--- a/core/parse-xml.cpp
+++ b/core/parse-xml.cpp
@@ -1410,7 +1410,7 @@ static void try_to_fill_trip(dive_trip_t *dive_trip, const char *name, char *buf
 /* We're processing a divesite entry - try to fill the components */
 static void try_to_fill_dive_site(struct parser_state *state, const char *name, char *buf)
 {
-	struct dive_site *ds = state->cur_dive_site;
+	auto &ds = state->cur_dive_site;
 	std::string taxonomy_value;
 
 	start_match("divesite", name, buf);
@@ -1423,7 +1423,7 @@ static void try_to_fill_dive_site(struct parser_state *state, const char *name,
 		return;
 	if (MATCH("notes", utf8_string, &ds->notes))
 		return;
-	if (MATCH("gps", gps_location, ds))
+	if (MATCH("gps", gps_location, ds.get()))
 		return;
 	if (MATCH("cat.geo", get_index, &state->taxonomy_category))
 		return;
@@ -1436,8 +1436,8 @@ static void try_to_fill_dive_site(struct parser_state *state, const char *name,
 		if (state->taxonomy_category < 0 || state->taxonomy_origin < 0) {
 			report_error("Warning: taxonomy value without origin or category");
 		} else {
-			taxonomy_set_category(&ds->taxonomy, (taxonomy_category)state->taxonomy_category,
-					      taxonomy_value.c_str(), (taxonomy_origin)state->taxonomy_origin);
+			taxonomy_set_category(ds->taxonomy, (taxonomy_category)state->taxonomy_category,
+					      taxonomy_value, (taxonomy_origin)state->taxonomy_origin);
 		}
 		state->taxonomy_category = state->taxonomy_origin = -1;
 		return;
diff --git a/core/parse.cpp b/core/parse.cpp
index bd48157d9..bb493d5a5 100644
--- a/core/parse.cpp
+++ b/core/parse.cpp
@@ -19,11 +19,14 @@
 #include "device.h"
 #include "gettext.h"
 
+parser_state::parser_state()
+{
+}
+
 parser_state::~parser_state()
 {
 	free_dive(cur_dive);
 	free_trip(cur_trip);
-	free_dive_site(cur_dive_site);
 }
 
 /*
@@ -188,7 +191,7 @@ void dive_site_start(struct parser_state *state)
 		return;
 	state->taxonomy_category = -1;
 	state->taxonomy_origin = -1;
-	state->cur_dive_site = (dive_site *)calloc(1, sizeof(struct dive_site));
+	state->cur_dive_site = std::make_unique<dive_site>();
 }
 
 void dive_site_end(struct parser_state *state)
@@ -197,13 +200,12 @@ void dive_site_end(struct parser_state *state)
 		return;
 
 	struct dive_site *ds = alloc_or_get_dive_site(state->cur_dive_site->uuid, state->log->sites);
-	merge_dive_site(ds, state->cur_dive_site);
+	merge_dive_site(ds, state->cur_dive_site.get());
 
 	if (verbose > 3)
 		printf("completed dive site uuid %x8 name {%s}\n", ds->uuid, ds->name);
 
-	free_dive_site(state->cur_dive_site);
-	state->cur_dive_site = NULL;
+	state->cur_dive_site.reset();
 }
 
 void filter_preset_start(struct parser_state *state)
diff --git a/core/parse.h b/core/parse.h
index ebd8af7a6..7cc1385e9 100644
--- a/core/parse.h
+++ b/core/parse.h
@@ -64,7 +64,7 @@ struct parser_state {
 
 	struct divecomputer *cur_dc = nullptr;			/* non-owning */
 	struct dive *cur_dive = nullptr;			/* owning */
-	struct dive_site *cur_dive_site = nullptr;		/* owning */
+	std::unique_ptr<dive_site> cur_dive_site;		/* owning */
 	location_t cur_location { 0 };
 	struct dive_trip *cur_trip = nullptr;			/* owning */
 	struct sample *cur_sample = nullptr;			/* non-owning */
@@ -96,6 +96,7 @@ struct parser_state {
 	sqlite3 *sql_handle = nullptr;				/* for SQL based parsers */
 	bool event_active = false;
 	event_allocation_t event_allocation;
+	parser_state();
 	~parser_state();
 };
 
diff --git a/core/pref.h b/core/pref.h
index b649ffc74..ac9c5e0b3 100644
--- a/core/pref.h
+++ b/core/pref.h
@@ -2,15 +2,15 @@
 #ifndef PREF_H
 #define PREF_H
 
+#include "units.h"
+#include "taxonomy.h"
+
 #ifdef __cplusplus
 extern "C" {
 #else
 #include <stdbool.h>
 #endif
 
-#include "units.h"
-#include "taxonomy.h"
-
 typedef struct
 {
 	bool po2;
diff --git a/core/save-git.cpp b/core/save-git.cpp
index ff838dda5..dbc4a21bb 100644
--- a/core/save-git.cpp
+++ b/core/save-git.cpp
@@ -933,11 +933,10 @@ static void save_divesites(git_repository *repo, struct dir *tree)
 		show_utf8(&b, "description ", ds->description, "\n");
 		show_utf8(&b, "notes ", ds->notes, "\n");
 		put_location(&b, &ds->location, "gps ", "\n");
-		for (int j = 0; j < ds->taxonomy.nr; j++) {
-			struct taxonomy *t = &ds->taxonomy.category[j];
-			if (t->category != TC_NONE && t->value) {
-				put_format(&b, "geo cat %d origin %d ", t->category, t->origin);
-				show_utf8(&b, "", t->value, "\n" );
+		for (const auto &t: ds->taxonomy) {
+			if (t.category != TC_NONE && !t.value.empty()) {
+				put_format(&b, "geo cat %d origin %d ", t.category, t.origin);
+				show_utf8(&b, "", t.value.c_str(), "\n" );
 			}
 		}
 		blob_insert(repo, subdir, &b, mb_cstring(&site_file_name));
diff --git a/core/save-xml.cpp b/core/save-xml.cpp
index 21ed7e089..5b4037875 100644
--- a/core/save-xml.cpp
+++ b/core/save-xml.cpp
@@ -716,15 +716,12 @@ static void save_dives_buffer(struct membuffer *b, bool select_only, bool anonym
 		show_utf8_blanked(b, ds->description, " description='", "'", 1, anonymize);
 		put_format(b, ">\n");
 		show_utf8_blanked(b, ds->notes, "  <notes>", " </notes>\n", 0, anonymize);
-		if (ds->taxonomy.nr) {
-			for (int j = 0; j < ds->taxonomy.nr; j++) {
-				struct taxonomy *t = &ds->taxonomy.category[j];
-				if (t->category != TC_NONE && t->value) {
-					put_format(b, "  <geo cat='%d'", t->category);
-					put_format(b, " origin='%d'", t->origin);
-					show_utf8_blanked(b, t->value, " value='", "'", 1, anonymize);
-					put_format(b, "/>\n");
-				}
+		for (auto const &t: ds->taxonomy) {
+			if (t.category != TC_NONE && !t.value.empty()) {
+				put_format(b, "  <geo cat='%d'", t.category);
+				put_format(b, " origin='%d'", t.origin);
+				show_utf8_blanked(b, t.value.c_str(), " value='", "'", 1, anonymize);
+				put_format(b, "/>\n");
 			}
 		}
 		put_format(b, "</site>\n");
@@ -921,15 +918,12 @@ static void save_dive_sites_buffer(struct membuffer *b, const struct dive_site *
 		show_utf8_blanked(b, ds->description, " description='", "'", 1, anonymize);
 		put_format(b, ">\n");
 		show_utf8_blanked(b, ds->notes, "  <notes>", " </notes>\n", 0, anonymize);
-		if (ds->taxonomy.nr) {
-			for (int j = 0; j < ds->taxonomy.nr; j++) {
-				struct taxonomy *t = &ds->taxonomy.category[j];
-				if (t->category != TC_NONE && t->value) {
-					put_format(b, "  <geo cat='%d'", t->category);
-					put_format(b, " origin='%d'", t->origin);
-					show_utf8_blanked(b, t->value, " value='", "'", 1, anonymize);
-					put_format(b, "/>\n");
-				}
+		for (const auto &t: ds->taxonomy) {
+			if (t.category != TC_NONE && !t.value.empty()) {
+				put_format(b, "  <geo cat='%d'", t.category);
+				put_format(b, " origin='%d'", t.origin);
+				show_utf8_blanked(b, t.value.c_str(), " value='", "'", 1, anonymize);
+				put_format(b, "/>\n");
 			}
 		}
 		put_format(b, "</site>\n");
diff --git a/core/string-format.cpp b/core/string-format.cpp
index 1dd9dd3e4..a81d35ae5 100644
--- a/core/string-format.cpp
+++ b/core/string-format.cpp
@@ -133,6 +133,12 @@ static void addStringToSortedList(QStringList &l, const std::string &s)
 	l.insert(it, qs);
 }
 
+// Safely treat null-strings. Remove once everyhting is converted to std::string
+static std::string c_to_std(const char *s)
+{
+	return s ? std::string(s) : std::string();
+}
+
 QStringList formatFullCylinderList()
 {
 	QStringList cylinders;
@@ -140,7 +146,7 @@ QStringList formatFullCylinderList()
 	int i = 0;
 	for_each_dive (i, d) {
 		for (int j = 0; j < d->cylinders.nr; j++)
-			addStringToSortedList(cylinders, get_cylinder(d, j)->type.description);
+			addStringToSortedList(cylinders, c_to_std(get_cylinder(d, j)->type.description));
 	}
 
 	for (const auto &ti: tank_info_table)
diff --git a/core/taxonomy.c b/core/taxonomy.c
deleted file mode 100644
index c21a2e644..000000000
--- a/core/taxonomy.c
+++ /dev/null
@@ -1,111 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include "taxonomy.h"
-#include "gettext.h"
-#include "subsurface-string.h"
-#include <stdlib.h>
-#include <stdio.h>
-
-char *taxonomy_category_names[TC_NR_CATEGORIES] = {
-	QT_TRANSLATE_NOOP("gettextFromC", "None"),
-	QT_TRANSLATE_NOOP("gettextFromC", "Ocean"),
-	QT_TRANSLATE_NOOP("gettextFromC", "Country"),
-	QT_TRANSLATE_NOOP("gettextFromC", "State"),
-	QT_TRANSLATE_NOOP("gettextFromC", "County"),
-	QT_TRANSLATE_NOOP("gettextFromC", "Town"),
-	QT_TRANSLATE_NOOP("gettextFromC", "City")
-};
-
-// these are the names for geoname.org
-char *taxonomy_api_names[TC_NR_CATEGORIES] = {
-	"none",
-	"name",
-	"countryName",
-	"adminName1",
-	"adminName2",
-	"toponymName",
-	"adminName3"
-};
-
-static void alloc_taxonomy_table(struct taxonomy_data *t)
-{
-	if (!t->category)
-		t->category = calloc(TC_NR_CATEGORIES, sizeof(struct taxonomy));
-}
-
-void free_taxonomy(struct taxonomy_data *t)
-{
-	if (t) {
-		for (int i = 0; i < t->nr; i++)
-			free((void *)t->category[i].value);
-		free(t->category);
-		t->category = NULL;
-		t->nr = 0;
-	}
-}
-
-void copy_taxonomy(const struct taxonomy_data *orig, struct taxonomy_data *copy)
-{
-	if (orig->category == NULL) {
-		free_taxonomy(copy);
-	} else {
-		alloc_taxonomy_table(copy);
-		for (int i = 0; i < TC_NR_CATEGORIES; i++) {
-			if (i < copy->nr) {
-				free((void *)copy->category[i].value);
-				copy->category[i].value = NULL;
-			}
-			if (i < orig->nr) {
-				copy->category[i] = orig->category[i];
-				copy->category[i].value = copy_string(orig->category[i].value);
-			}
-		}
-		copy->nr = orig->nr;
-	}
-}
-
-static int taxonomy_index_for_category(const struct taxonomy_data *t, enum taxonomy_category cat)
-{
-	for (int i = 0; i < t->nr; i++) {
-		if (t->category[i].category == cat)
-			return i;
-	}
-	return -1;
-}
-
-const char *taxonomy_get_value(const struct taxonomy_data *t, enum taxonomy_category cat)
-{
-	int idx = taxonomy_index_for_category(t, cat);
-	return idx >= 0 ? t->category[idx].value : NULL;
-}
-
-const char *taxonomy_get_country(const struct taxonomy_data *t)
-{
-	return taxonomy_get_value(t, TC_COUNTRY);
-}
-
-void taxonomy_set_category(struct taxonomy_data *t, enum taxonomy_category category, const char *value, enum taxonomy_origin origin)
-{
-	int idx = taxonomy_index_for_category(t, category);
-
-	if (idx < 0) {
-		alloc_taxonomy_table(t); // make sure we have taxonomy data allocated
-		if (t->nr == TC_NR_CATEGORIES - 1) {
-			// can't add another one
-			fprintf(stderr, "Error adding taxonomy category\n");
-			return;
-		}
-		idx = t->nr++;
-	} else {
-		free((void *)t->category[idx].value);
-		t->category[idx].value = NULL;
-	}
-	t->category[idx].value = strdup(value);
-	t->category[idx].origin = origin;
-	t->category[idx].category = category;
-}
-
-void taxonomy_set_country(struct taxonomy_data *t, const char *country, enum taxonomy_origin origin)
-{
-	fprintf(stderr, "%s: set the taxonomy country to %s\n", __func__, country);
-	taxonomy_set_category(t, TC_COUNTRY, country, origin);
-}
diff --git a/core/taxonomy.cpp b/core/taxonomy.cpp
new file mode 100644
index 000000000..18f589b6b
--- /dev/null
+++ b/core/taxonomy.cpp
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "taxonomy.h"
+#include "errorhelper.h"
+#include "gettext.h"
+#include "subsurface-string.h"
+#include <algorithm>
+#include <stdlib.h>
+#include <stdio.h>
+#include <QtGlobal> // for QT_TRANSLATE_NOOP
+
+const char *taxonomy_category_names[TC_NR_CATEGORIES] = {
+	QT_TRANSLATE_NOOP("gettextFromC", "None"),
+	QT_TRANSLATE_NOOP("gettextFromC", "Ocean"),
+	QT_TRANSLATE_NOOP("gettextFromC", "Country"),
+	QT_TRANSLATE_NOOP("gettextFromC", "State"),
+	QT_TRANSLATE_NOOP("gettextFromC", "County"),
+	QT_TRANSLATE_NOOP("gettextFromC", "Town"),
+	QT_TRANSLATE_NOOP("gettextFromC", "City")
+};
+
+// these are the names for geoname.org
+const char *taxonomy_api_names[TC_NR_CATEGORIES] = {
+	"none",
+	"name",
+	"countryName",
+	"adminName1",
+	"adminName2",
+	"toponymName",
+	"adminName3"
+};
+
+std::string taxonomy_get_value(const taxonomy_data &t, enum taxonomy_category cat)
+{
+	auto it = std::find_if(t.begin(), t.end(), [cat] (const taxonomy &tax) { return tax.category == cat; });
+	return it != t.end() ? it->value : std::string();
+}
+
+std::string taxonomy_get_country(const taxonomy_data &t)
+{
+	return taxonomy_get_value(t, TC_COUNTRY);
+}
+
+void taxonomy_set_category(taxonomy_data &t, enum taxonomy_category cat, const std::string &value, enum taxonomy_origin origin)
+{
+	auto it = std::find_if(t.begin(), t.end(), [cat] (const taxonomy &tax) { return tax.category == cat; });
+	if (it == t.end()) {
+		t.emplace_back();
+		it = std::prev(t.end());
+	}
+	it->value = value;
+	it->origin = origin;
+	it->category = cat;
+}
+
+void taxonomy_set_country(taxonomy_data &t, const std::string &country, enum taxonomy_origin origin)
+{
+	report_info("%s: set the taxonomy country to %s\n", __func__, country.c_str());
+	taxonomy_set_category(t, TC_COUNTRY, country, origin);
+}
diff --git a/core/taxonomy.h b/core/taxonomy.h
index a2c03220c..6c61d91e1 100644
--- a/core/taxonomy.h
+++ b/core/taxonomy.h
@@ -3,8 +3,9 @@
 #define TAXONOMY_H
 
 #ifdef __cplusplus
-extern "C" {
-#endif
+
+#include <string>
+#include <vector>
 
 enum taxonomy_category {
 	TC_NONE,
@@ -24,29 +25,22 @@ enum taxonomy_origin {
 	GEOCOPIED
 };
 
-extern char *taxonomy_category_names[TC_NR_CATEGORIES];
-extern char *taxonomy_api_names[TC_NR_CATEGORIES];
+extern const char *taxonomy_category_names[TC_NR_CATEGORIES];
+extern const char *taxonomy_api_names[TC_NR_CATEGORIES];
 
 struct taxonomy {
-	int category;		/* the category for this tag: ocean, country, admin_l1, admin_l2, localname, etc */
-	const char *value;	/* the value returned, parsed, or manually entered for that category */
-	enum taxonomy_origin origin;
+	taxonomy_category category;	/* the category for this tag: ocean, country, admin_l1, admin_l2, localname, etc */
+	std::string value;		/* the value returned, parsed, or manually entered for that category */
+	taxonomy_origin origin;
 };
 
-/* the data block contains 3 taxonomy structures - unused ones have a tag value of NONE */
-struct taxonomy_data {
-	int nr;
-	struct taxonomy *category;
-};
+/* the data block contains taxonomy structures - unused ones have a tag value of NONE */
+using taxonomy_data = std::vector<taxonomy>;
 
-void free_taxonomy(struct taxonomy_data *t);
-void copy_taxonomy(const struct taxonomy_data *orig, struct taxonomy_data *copy);
-const char *taxonomy_get_value(const struct taxonomy_data *t, enum taxonomy_category cat);
-const char *taxonomy_get_country(const struct taxonomy_data *t);
-void taxonomy_set_category(struct taxonomy_data *t, enum taxonomy_category category, const char *value, enum taxonomy_origin origin);
-void taxonomy_set_country(struct taxonomy_data *t, const char *country, enum taxonomy_origin origin);
+std::string taxonomy_get_value(const taxonomy_data &t, enum taxonomy_category cat);
+std::string taxonomy_get_country(const taxonomy_data &t);
+void taxonomy_set_category(taxonomy_data &t, enum taxonomy_category category, const std::string &value, enum taxonomy_origin origin);
+void taxonomy_set_country(taxonomy_data &t, const std::string &country, enum taxonomy_origin origin);
 
-#ifdef __cplusplus
-}
 #endif
 #endif // TAXONOMY_H
diff --git a/core/uploadDiveLogsDE.cpp b/core/uploadDiveLogsDE.cpp
index 0589934c2..e4660a94f 100644
--- a/core/uploadDiveLogsDE.cpp
+++ b/core/uploadDiveLogsDE.cpp
@@ -116,13 +116,14 @@ bool uploadDiveLogsDE::prepareDives(const QString &tempfile, bool selected)
 			put_format(&mb, "'");
 			put_location(&mb, &ds->location, " gps='", "'");
 			put_format(&mb, ">\n");
-			if (ds->taxonomy.nr) {
-				for (int j = 0; j < ds->taxonomy.nr; j++) {
-					struct taxonomy *t = &ds->taxonomy.category[j];
-					if (t->category != TC_NONE && t->category == prefs.geocoding.category[j] && t->value) {
-						put_format(&mb, "  <geo cat='%d'", t->category);
-						put_format(&mb, " origin='%d' value='", t->origin);
-						put_quoted(&mb, t->value, 1, 0);
+			for (int i = 0; i < 3; i++) {
+				if (prefs.geocoding.category[i] == TC_NONE)
+					continue;
+				for (auto const &t: ds->taxonomy) {
+					if (t.category == prefs.geocoding.category[i] && !t.value.empty()) {
+						put_format(&mb, "  <geo cat='%d'", t.category);
+						put_format(&mb, " origin='%d' value='", t.origin);
+						put_quoted(&mb, t.value.c_str(), 1, 0);
 						put_format(&mb, "'/>\n");
 					}
 				}
diff --git a/desktop-widgets/divesiteimportdialog.cpp b/desktop-widgets/divesiteimportdialog.cpp
index 66f003253..3ab94732e 100644
--- a/desktop-widgets/divesiteimportdialog.cpp
+++ b/desktop-widgets/divesiteimportdialog.cpp
@@ -67,7 +67,7 @@ void DivesiteImportDialog::on_ok_clicked()
 	struct dive_site_table selectedSites = empty_dive_site_table;
 	for (int i = 0; i < importedSites.nr; i++)
 		if (divesiteImportedModel->data(divesiteImportedModel->index(i, 0), Qt::CheckStateRole) == Qt::Checked) {
-			struct dive_site *newSite = alloc_dive_site();
+			struct dive_site *newSite = new dive_site;
 			copy_dive_site(importedSites.dive_sites[i], newSite);
 			add_dive_site_to_table(newSite, &selectedSites);
 		}
diff --git a/desktop-widgets/locationinformation.cpp b/desktop-widgets/locationinformation.cpp
index d2ece3f17..dc5131da4 100644
--- a/desktop-widgets/locationinformation.cpp
+++ b/desktop-widgets/locationinformation.cpp
@@ -133,9 +133,9 @@ void LocationInformationWidget::updateLabels()
 		ui.diveSiteName->setText(diveSite->name);
 	else
 		ui.diveSiteName->clear();
-	const char *country = taxonomy_get_country(&diveSite->taxonomy);
-	if (country)
-		ui.diveSiteCountry->setText(country);
+	std::string country = taxonomy_get_country(diveSite->taxonomy);
+	if (!country.empty())
+		ui.diveSiteCountry->setText(QString::fromStdString(country));
 	else
 		ui.diveSiteCountry->clear();
 	if (diveSite->description)
@@ -152,7 +152,7 @@ void LocationInformationWidget::updateLabels()
 		ui.diveSiteCoordinates->clear();
 	coordinatesSetWarning(false);
 
-	ui.locationTags->setText(constructLocationTags(&diveSite->taxonomy, false));
+	ui.locationTags->setText(constructLocationTags(diveSite->taxonomy, false));
 }
 
 void LocationInformationWidget::unitsChanged()
@@ -181,8 +181,8 @@ void LocationInformationWidget::diveSiteChanged(struct dive_site *ds, int field)
 		ui.diveSiteNotes->setText(diveSite->notes);
 		return;
 	case LocationInformationModel::TAXONOMY:
-		ui.diveSiteCountry->setText(taxonomy_get_country(&diveSite->taxonomy));
-		ui.locationTags->setText(constructLocationTags(&diveSite->taxonomy, false));
+		ui.diveSiteCountry->setText(QString::fromStdString(taxonomy_get_country(diveSite->taxonomy)));
+		ui.locationTags->setText(constructLocationTags(diveSite->taxonomy, false));
 		return;
 	case LocationInformationModel::LOCATION:
 		filter_model.setCoordinates(diveSite->location);
@@ -342,10 +342,8 @@ void LocationInformationWidget::reverseGeocode()
 	if (!ds || !has_location(&location))
 		return;
 	taxonomy_data taxonomy = reverseGeoLookup(location.lat, location.lon);
-	if (ds != diveSite) {
-		free_taxonomy(&taxonomy);
+	if (ds != diveSite)
 		return;
-	}
 	// This call transfers ownership of the taxonomy memory into an EditDiveSiteTaxonomy object
 	Command::editDiveSiteTaxonomy(ds, taxonomy);
 }
diff --git a/desktop-widgets/modeldelegates.cpp b/desktop-widgets/modeldelegates.cpp
index 5317e413a..e9365c365 100644
--- a/desktop-widgets/modeldelegates.cpp
+++ b/desktop-widgets/modeldelegates.cpp
@@ -461,12 +461,12 @@ void LocationFilterDelegate::paint(QPainter *painter, const QStyleOptionViewItem
 	for (int i = 0; i < 3; i++) {
 		if (prefs.geocoding.category[i] == TC_NONE)
 			continue;
-		const char *value = taxonomy_get_value(&ds->taxonomy, prefs.geocoding.category[i]);
-		if (empty_string(value))
+		std::string value = taxonomy_get_value(ds->taxonomy, prefs.geocoding.category[i]);
+		if (!value.empty())
 			continue;
 		if(!bottomText.isEmpty())
 			bottomText += " / ";
-		bottomText += QString(value);
+		bottomText += QString::fromStdString(value);
 	}
 
 	if (bottomText.isEmpty())
diff --git a/desktop-widgets/tab-widgets/TabDiveNotes.cpp b/desktop-widgets/tab-widgets/TabDiveNotes.cpp
index 8135cd8af..3e4b995e5 100644
--- a/desktop-widgets/tab-widgets/TabDiveNotes.cpp
+++ b/desktop-widgets/tab-widgets/TabDiveNotes.cpp
@@ -178,7 +178,7 @@ void TabDiveNotes::updateDiveSite(struct dive *d)
 	struct dive_site *ds = d->dive_site;
 	ui.location->setCurrentDiveSite(d);
 	if (ds) {
-		ui.locationTags->setText(constructLocationTags(&ds->taxonomy, true));
+		ui.locationTags->setText(constructLocationTags(ds->taxonomy, true));
 
 		if (ui.locationTags->text().isEmpty() && has_location(&ds->location))
 			ui.locationTags->setText(printGPSCoords(&ds->location));
diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp
index 7d75a55c3..685696a4a 100644
--- a/mobile-widgets/qmlmanager.cpp
+++ b/mobile-widgets/qmlmanager.cpp
@@ -908,7 +908,7 @@ void QMLManager::refreshDiveList()
 // The following structure describes such a change caused by a dive edit.
 // Hopefully, we can remove this in due course by using finer-grained undo-commands.
 struct DiveSiteChange {
-	OwningDiveSitePtr createdDs; // not-null if we created a dive site.
+	std::unique_ptr<dive_site> createdDs; // not-null if we created a dive site.
 
 	dive_site *editDs = nullptr; // not-null if we are supposed to edit an existing dive site.
 	location_t location = zero_location; // new value of the location if we edit an existing dive site.
@@ -923,7 +923,7 @@ static void setupDivesite(DiveSiteChange &res, struct dive *d, struct dive_site
 		res.editDs = ds;
 		res.location = location;
 	} else {
-		res.createdDs.reset(alloc_dive_site_with_name(locationtext));
+		res.createdDs = std::make_unique<dive_site>(locationtext);
 		res.createdDs->location = location;
 		d->dive_site = res.createdDs.get();
 	}
@@ -1072,7 +1072,7 @@ bool QMLManager::checkLocation(DiveSiteChange &res, struct dive *d, QString loca
 	if (oldLocation != location) {
 		ds = get_dive_site_by_name(qPrintable(location), divelog.sites);
 		if (!ds && !location.isEmpty()) {
-			res.createdDs.reset(alloc_dive_site_with_name(qPrintable(location)));
+			res.createdDs = std::make_unique<dive_site>(qPrintable(location));
 			res.changed = true;
 			ds = res.createdDs.get();
 		}
diff --git a/qt-models/divesiteimportmodel.cpp b/qt-models/divesiteimportmodel.cpp
index 07c763bb5..4854fbb6b 100644
--- a/qt-models/divesiteimportmodel.cpp
+++ b/qt-models/divesiteimportmodel.cpp
@@ -63,7 +63,7 @@ QVariant DivesiteImportedModel::data(const QModelIndex &index, int role) const
 		case LOCATION:
 			return printGPSCoords(&ds->location);
 		case COUNTRY:
-			return taxonomy_get_country(&ds->taxonomy);
+			return QString::fromStdString(taxonomy_get_country(ds->taxonomy));
 		case NEAREST: {
 			// 40075000 is circumference of the earth in meters
 			struct dive_site *nearest_ds =
diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp
index 12929b605..0ddc0e8bb 100644
--- a/qt-models/divetripmodel.cpp
+++ b/qt-models/divetripmodel.cpp
@@ -351,7 +351,7 @@ QVariant DiveTripModelBase::diveData(const struct dive *d, int column, int role)
 		case PHOTOS:
 			break;
 		case COUNTRY:
-			return QString(get_dive_country(d));
+			return QString::fromStdString(get_dive_country(d));
 		case BUDDIES:
 			return QString(d->buddy);
 		case DIVEGUIDE:
@@ -1770,7 +1770,7 @@ bool DiveTripModelList::lessThan(const QModelIndex &i1, const QModelIndex &i2) c
 	case PHOTOS:
 		return lessThanHelper(countPhotos(d1) - countPhotos(d2), row_diff);
 	case COUNTRY:
-		return lessThanHelper(strCmp(get_dive_country(d1), get_dive_country(d2)), row_diff);
+		return lessThanHelper(strCmp(get_dive_country(d1).c_str(), get_dive_country(d2).c_str()), row_diff);
 	case BUDDIES:
 		return lessThanHelper(strCmp(d1->buddy, d2->buddy), row_diff);
 	case DIVEGUIDE: