From b20a091e5cfcbcb62f5ef1bcfb778026a3d3c1d2 Mon Sep 17 00:00:00 2001
From: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Date: Thu, 2 May 2024 21:26:22 +0200
Subject: [PATCH] import: turn C-string in device_data_t into std::strings

It was never clear what was a pointer to a static string from
libdivecomputer and what was allocated.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
---
 core/configuredivecomputer.cpp                |  6 +-
 core/datatrak.cpp                             | 10 +--
 core/downloadfromdcthread.cpp                 | 16 ++---
 core/libdivecomputer.cpp                      | 61 +++++++------------
 core/libdivecomputer.h                        | 14 ++---
 core/ostctools.cpp                            |  2 +-
 core/qt-ble.cpp                               | 51 +++++++++++-----
 core/qt-ble.h                                 | 13 ++--
 core/uemis-downloader.cpp                     | 12 ++--
 .../configuredivecomputerdialog.cpp           | 10 +--
 smtk-import/smartrak.cpp                      |  9 ++-
 11 files changed, 99 insertions(+), 105 deletions(-)

diff --git a/core/configuredivecomputer.cpp b/core/configuredivecomputer.cpp
index 0a7eb3d3a..31444a8ca 100644
--- a/core/configuredivecomputer.cpp
+++ b/core/configuredivecomputer.cpp
@@ -68,16 +68,14 @@ static QString writeGasDetails(gas g)
 bool ConfigureDiveComputer::saveXMLBackup(const QString &fileName, const DeviceDetails &details, device_data_t *data)
 {
 	QString xml = "";
-	QString vendor = data->vendor;
-	QString product = data->product;
 	QXmlStreamWriter writer(&xml);
 	writer.setAutoFormatting(true);
 
 	writer.writeStartDocument();
 	writer.writeStartElement("DiveComputerSettingsBackup");
 	writer.writeStartElement("DiveComputer");
-	writer.writeTextElement("Vendor", vendor);
-	writer.writeTextElement("Product", product);
+	writer.writeTextElement("Vendor", QString::fromStdString(data->vendor));
+	writer.writeTextElement("Product", QString::fromStdString(data->product));
 	writer.writeEndElement();
 	writer.writeStartElement("Settings");
 	writer.writeTextElement("CustomText", details.customText);
diff --git a/core/datatrak.cpp b/core/datatrak.cpp
index 09190bf3f..30bd8a5aa 100644
--- a/core/datatrak.cpp
+++ b/core/datatrak.cpp
@@ -111,10 +111,12 @@ static int dtrak_prepare_data(int model, device_data_t &dev_data)
 
 	while (model != g_models[i].model_num && g_models[i].model_num != 0xEE)
 		i++;
-	dev_data.model = copy_string(g_models[i].name);
+	dev_data.model = g_models[i].name;
 	dev_data.vendor = (const char *)malloc(strlen(g_models[i].name) + 1);
-	sscanf(g_models[i].name, "%[A-Za-z] ", (char *)dev_data.vendor);
-	dev_data.product = copy_string(strchr(g_models[i].name, ' ') + 1);
+	dev_data.vendor.clear();
+	for (const char *s = g_models[i].name; isalpha(*s); ++s)
+		dev_data.vendor += *s;
+	dev_data.product = strchr(g_models[i].name, ' ') + 1;
 
 	d = get_descriptor(g_models[i].type, g_models[i].libdc_num);
 	if (d)
@@ -520,7 +522,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
 	libdc_model = dtrak_prepare_data(tmp_1byte, devdata);
 	if (!libdc_model)
 		report_error(translate("gettextFromC", "[Warning] Manual dive # %d\n"), dt_dive->number);
-	dt_dive->dc.model = copy_string(devdata.model);
+	dt_dive->dc.model = copy_string(devdata.model.c_str());
 
 	/*
 	 * Air usage, unknown use. Probably allows or deny manually entering gas
diff --git a/core/downloadfromdcthread.cpp b/core/downloadfromdcthread.cpp
index a536c85f2..ad9c672d1 100644
--- a/core/downloadfromdcthread.cpp
+++ b/core/downloadfromdcthread.cpp
@@ -81,7 +81,7 @@ void DownloadThread::run()
 	auto internalData = m_data->internalData();
 	internalData->descriptor = descriptorLookup[m_data->vendor().toLower() + m_data->product().toLower()];
 	internalData->log = &log;
-	internalData->btname = strdup(m_data->devBluetoothName().toUtf8());
+	internalData->btname = m_data->devBluetoothName().toStdString();
 	if (!internalData->descriptor) {
 		report_info("No download possible when DC type is unknown");
 		return;
@@ -103,7 +103,7 @@ void DownloadThread::run()
 	std::string errorText;
 	import_thread_cancelled = false;
 	error.clear();
-	if (!strcmp(internalData->vendor, "Uemis"))
+	if (internalData->vendor == "Uemis")
 		errorText = do_uemis_import(internalData);
 	else
 		errorText = do_libdivecomputer_import(internalData);
@@ -116,9 +116,9 @@ void DownloadThread::run()
 			error = tr("No new dives downloaded from dive computer").toStdString();
 		report_info("Finishing download thread: %d dives downloaded", log.dives->nr);
 	}
-	qPrefDiveComputer::set_vendor(internalData->vendor);
-	qPrefDiveComputer::set_product(internalData->product);
-	qPrefDiveComputer::set_device(internalData->devname);
+	qPrefDiveComputer::set_vendor(internalData->vendor.c_str());
+	qPrefDiveComputer::set_product(internalData->product.c_str());
+	qPrefDiveComputer::set_device(internalData->devname.c_str());
 	qPrefDiveComputer::set_device_name(m_data->devBluetoothName());
 
 	updateRememberedDCs();
@@ -246,17 +246,17 @@ DCDeviceData *DownloadThread::data()
 
 QString DCDeviceData::vendor() const
 {
-	return data.vendor;
+	return QString::fromStdString(data.vendor);
 }
 
 QString DCDeviceData::product() const
 {
-	return data.product;
+	return QString::fromStdString(data.product);
 }
 
 QString DCDeviceData::devName() const
 {
-	return data.devname;
+	return QString::fromStdString(data.devname);
 }
 
 QString DCDeviceData::devBluetoothName() const
diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp
index 692c6f0e7..e3b2bc1e2 100644
--- a/core/libdivecomputer.cpp
+++ b/core/libdivecomputer.cpp
@@ -40,6 +40,7 @@
 #include "core/qthelper.h"
 #include "core/file.h"
 #include <array>
+#include <charconv>
 
 std::string dumpfile_name;
 std::string logfile_name;
@@ -226,7 +227,7 @@ static dc_status_t parse_gasmixes(device_data_t *devdata, struct dive *dive, dc_
 				cyl.type.workingpressure.mbar = lrint(tank.workpressure * 1000);
 
 				if (tank.type & DC_TANKVOLUME_IMPERIAL) {
-					if (same_string(devdata->model, "Suunto EON Steel")) {
+					if (devdata->model == "Suunto EON Steel") {
 						/* Suunto EON Steele gets this wrong. Badly.
 						 * but on the plus side it only supports a few imperial sizes,
 						 * so let's try and guess at least the most common ones.
@@ -279,7 +280,7 @@ static dc_status_t parse_gasmixes(device_data_t *devdata, struct dive *dive, dc_
 				if (!nearly_0(tank.endpressure)) {
 					cyl.start.mbar = lrint(tank.beginpressure * 1000);
 					cyl.end.mbar = lrint(tank.endpressure * 1000);
-				} else if (same_string(devdata->vendor, "Uwatec")) {
+				} else if (devdata->vendor == "Uwatec") {
 					cyl.start.mbar = lrint(tank.beginpressure * 1000 + 30000);
 					cyl.end.mbar = 30000;
 				}
@@ -577,22 +578,6 @@ static int find_dive(struct divecomputer *match)
 	return 0;
 }
 
-/*
- * Like g_strdup_printf(), but without the stupid g_malloc/g_free confusion.
- * And we limit the string to some arbitrary size.
- */
-static char *str_printf(const char *fmt, ...)
-{
-	va_list args;
-	char buf[1024];
-
-	va_start(args, fmt);
-	vsnprintf(buf, sizeof(buf) - 1, fmt, args);
-	va_end(args);
-	buf[sizeof(buf) - 1] = 0;
-	return strdup(buf);
-}
-
 /*
  * The dive ID for libdivecomputer dives is the first word of the
  * SHA1 of the fingerprint, if it exists.
@@ -827,14 +812,14 @@ static int dive_cb(const unsigned char *data, unsigned int size,
 
 	rc = dc_parser_new(&parser, devdata->device, data, size);
 	if (rc != DC_STATUS_SUCCESS) {
-		download_error(translate("gettextFromC", "Unable to create parser for %s %s: %d"), devdata->vendor, devdata->product, errmsg(rc));
+		download_error(translate("gettextFromC", "Unable to create parser for %s %s: %d"), devdata->vendor.c_str(), devdata->product.c_str(), errmsg(rc));
 		return true;
 	}
 
 	dive = alloc_dive();
 
 	// Fill in basic fields
-	dive->dc.model = strdup(devdata->model);
+	dive->dc.model = strdup(devdata->model.c_str());
 	dive->dc.diveid = calculate_diveid(fingerprint, fsize);
 
 	// Parse the dive's header data
@@ -939,7 +924,7 @@ static std::string fingerprint_file(device_data_t *devdata)
 	uint32_t model, serial;
 
 	// Model hash and libdivecomputer 32-bit 'serial number' for the file name
-	model = calculate_string_hash(devdata->model);
+	model = calculate_string_hash(devdata->model.c_str());
 	serial = devdata->devinfo.serial;
 
 	return format_string_std("%s/fingerprints/%04x.%u",
@@ -1038,7 +1023,7 @@ static void lookup_fingerprint(dc_device_t *device, device_data_t *devdata)
 		return;
 
 	/* first try our in memory data - raw_data is owned by the table, the dc_device_set_fingerprint function copies the data */
-	int fsize = get_fingerprint_data(&fingerprint_table, calculate_string_hash(devdata->model), devdata->devinfo.serial, &raw_data);
+	int fsize = get_fingerprint_data(&fingerprint_table, calculate_string_hash(devdata->model.c_str()), devdata->devinfo.serial, &raw_data);
 	if (fsize) {
 		if (verbose)
 			dev_info(devdata, "... found fingerprint in dive table");
@@ -1094,14 +1079,14 @@ static void event_cb(dc_device_t *device, dc_event_type_t event, const void *dat
 				devdata->descriptor = better_descriptor;
 				devdata->product = dc_descriptor_get_product(better_descriptor);
 				devdata->vendor = dc_descriptor_get_vendor(better_descriptor);
-				devdata->model = str_printf("%s %s", devdata->vendor, devdata->product);
+				devdata->model = devdata->vendor + " " + devdata->product;
 			} else {
 				report_info("EVENT_DEVINFO gave us a different detected product (model %d instead of %d), but that one is unknown.",
 					devinfo->model, dc_descriptor_get_model(devdata->descriptor));
 			}
 		}
 		dev_info(devdata, translate("gettextFromC", "model=%s firmware=%u serial=%u"),
-			 devdata->product, devinfo->firmware, devinfo->serial);
+			 devdata->product.c_str(), devinfo->firmware, devinfo->serial);
 		if (devdata->libdc_logfile) {
 			fprintf(devdata->libdc_logfile, "Event: model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)\n",
 				devinfo->model, devinfo->model,
@@ -1146,7 +1131,7 @@ static std::string do_device_import(device_data_t *data)
 	dc_status_t rc;
 	dc_device_t *device = data->device;
 
-	data->model = str_printf("%s %s", data->vendor, data->product);
+	data->model = data->vendor + " " + data->product;
 
 	// Register the event handler.
 	int events = DC_EVENT_WAITING | DC_EVENT_PROGRESS | DC_EVENT_DEVINFO | DC_EVENT_CLOCK | DC_EVENT_VENDOR;
@@ -1260,7 +1245,7 @@ unsigned int get_supported_transports(device_data_t *data)
 		 */
 		if (data->bluetooth_mode) {
 			supported &= (DC_TRANSPORT_BLUETOOTH | DC_TRANSPORT_BLE);
-			if (!strncmp(data->devname, "LE:", 3))
+			if (starts_with(data->devname, "LE:"))
 				supported &= DC_TRANSPORT_BLE;
 		} else {
 			supported &= ~(DC_TRANSPORT_BLUETOOTH | DC_TRANSPORT_BLE);
@@ -1334,7 +1319,7 @@ static dc_status_t irda_device_open(dc_iostream_t **iostream, dc_context_t *cont
 	// If that fails, use the device name. This will
 	// use address 0 if it's not a number.
 	if (!address)
-		address = strtoul(data->devname, NULL, 0);
+		std::from_chars(data->devname.c_str(), data->devname.c_str() + data->devname.size(), address);
 
 	dev_info(data, "Opening IRDA address %u", address);
 	return dc_irda_open(&data->iostream, context, address, 1);
@@ -1383,10 +1368,10 @@ dc_status_t divecomputer_device_open(device_data_t *data)
 
 #ifdef BT_SUPPORT
 	if (transports & DC_TRANSPORT_BLUETOOTH) {
-		dev_info(data, "Opening rfcomm stream %s", data->devname);
+		dev_info(data, "Opening rfcomm stream %s", data->devname.c_str());
 #if defined(__ANDROID__) || defined(__APPLE__)
 		// we don't have BT on iOS in the first place, so this is for Android and macOS
-		rc = rfcomm_stream_open(&data->iostream, context, data->devname);
+		rc = rfcomm_stream_open(&data->iostream, context, data->devname.c_str());
 #else
 		rc = bluetooth_device_open(context, data);
 #endif
@@ -1397,8 +1382,8 @@ dc_status_t divecomputer_device_open(device_data_t *data)
 
 #ifdef BLE_SUPPORT
 	if (transports & DC_TRANSPORT_BLE) {
-		dev_info(data, "Connecting to BLE device %s", data->devname);
-		rc = ble_packet_open(&data->iostream, context, data->devname, data);
+		dev_info(data, "Connecting to BLE device %s", data->devname.c_str());
+		rc = ble_packet_open(&data->iostream, context, data->devname.c_str(), data);
 		if (rc == DC_STATUS_SUCCESS)
 			return rc;
 	}
@@ -1419,16 +1404,16 @@ dc_status_t divecomputer_device_open(device_data_t *data)
 	}
 
 	if (transports & DC_TRANSPORT_SERIAL) {
-		dev_info(data, "Opening serial device %s", data->devname);
+		dev_info(data, "Opening serial device %s", data->devname.c_str());
 #ifdef SERIAL_FTDI
-		if (!strcasecmp(data->devname, "ftdi"))
+		if (!strcasecmp(data->devname.c_str(), "ftdi"))
 			return ftdi_open(&data->iostream, context);
 #endif
 #ifdef __ANDROID__
 		if (data->androidUsbDeviceDescriptor)
 			return serial_usb_android_open(&data->iostream, context, data->androidUsbDeviceDescriptor);
 #endif
-		rc = dc_serial_open(&data->iostream, context, data->devname);
+		rc = dc_serial_open(&data->iostream, context, data->devname.c_str());
 		if (rc == DC_STATUS_SUCCESS)
 			return rc;
 
@@ -1442,8 +1427,8 @@ dc_status_t divecomputer_device_open(device_data_t *data)
 	}
 
 	if (transports & DC_TRANSPORT_USBSTORAGE) {
-		dev_info(data, "Opening USB storage at %s", data->devname);
-		rc = dc_usb_storage_open(&data->iostream, context, data->devname);
+		dev_info(data, "Opening USB storage at %s", data->devname.c_str());
+		rc = dc_usb_storage_open(&data->iostream, context, data->devname.c_str());
 		if (rc == DC_STATUS_SUCCESS)
 			return rc;
 	}
@@ -1499,7 +1484,7 @@ std::string do_libdivecomputer_import(device_data_t *data)
 		rc = dc_device_open(&data->device, data->context, data->descriptor, data->iostream);
 		if (rc != DC_STATUS_SUCCESS) {
 			INFO("dc_device_open error value of %d", rc);
-			if (subsurface_access(data->devname, R_OK | W_OK) != 0)
+			if (subsurface_access(data->devname.c_str(), R_OK | W_OK) != 0)
 #if defined(SUBSURFACE_MOBILE)
 				err = translate("gettextFromC", "Error opening the device %s %s (%s).\nIn most cases, in order to debug this issue, it is useful to send the developers the log files. You can copy them to the clipboard in the About dialog.");
 #else
@@ -1560,7 +1545,7 @@ std::string do_libdivecomputer_import(device_data_t *data)
 	 */
 	save_fingerprint(data);
 	if (data->fingerprint && data->fdiveid)
-		create_fingerprint_node(&fingerprint_table, calculate_string_hash(data->model), data->devinfo.serial,
+		create_fingerprint_node(&fingerprint_table, calculate_string_hash(data->model.c_str()), data->devinfo.serial,
 					data->fingerprint, data->fsize, data->fdeviceid, data->fdiveid);
 	free(data->fingerprint);
 	data->fingerprint = NULL;
diff --git a/core/libdivecomputer.h b/core/libdivecomputer.h
index 556b83dff..9556edd3b 100644
--- a/core/libdivecomputer.h
+++ b/core/libdivecomputer.h
@@ -21,10 +21,9 @@
 #endif
 
 #ifdef __cplusplus
+#include <string>
+
 extern "C" {
-#else
-#include <stdbool.h>
-#endif
 
 struct dive;
 struct divelog;
@@ -32,8 +31,8 @@ struct devices;
 
 typedef struct {
 	dc_descriptor_t *descriptor = nullptr;
-	const char *vendor = nullptr, *product = nullptr, *devname = nullptr;
-	const char *model = nullptr, *btname = nullptr;
+	std::string vendor, product, devname;
+	std::string model, btname;
 	unsigned char *fingerprint = nullptr;
 	unsigned int fsize = 0, fdeviceid = 0, fdiveid = 0;
 	struct dc_event_devinfo_t devinfo = { };
@@ -71,13 +70,10 @@ dc_status_t divecomputer_device_open(device_data_t *data);
 
 unsigned int get_supported_transports(device_data_t *data);
 
-#ifdef __cplusplus
-}
-
-#include <string>
 extern std::string logfile_name;
 extern std::string dumpfile_name;
 
+}
 #endif
 
 #endif // LIBDIVECOMPUTER_H
diff --git a/core/ostctools.cpp b/core/ostctools.cpp
index 17607302d..73201e071 100644
--- a/core/ostctools.cpp
+++ b/core/ostctools.cpp
@@ -153,7 +153,7 @@ extern "C" void ostctools_import(const char *file, struct divelog *log)
 		report_error(translate("gettextFromC", "Unknown DC in dive %d"), ostcdive->number);
 		return;
 	}
-	std::string tmp = format_string_std("%s %s (Imported from OSTCTools)", devdata.vendor, devdata.model);
+	std::string tmp = devdata.vendor + " " + devdata.model + " (Imported from OSTCTools)";
 	ostcdive->dc.model = copy_string(tmp.c_str());
 
 	// Parse the dive data
diff --git a/core/qt-ble.cpp b/core/qt-ble.cpp
index 3e00c205d..d72864add 100644
--- a/core/qt-ble.cpp
+++ b/core/qt-ble.cpp
@@ -25,10 +25,6 @@
 #define DEBUG_THRESHOLD 50
 static int debugCounter;
 
-#define IS_HW(_d) same_string((_d)->vendor, "Heinrichs Weikamp")
-#define IS_SHEARWATER(_d) same_string((_d)->vendor, "Shearwater")
-#define IS_GARMIN(_d) same_string((_d)->vendor, "Garmin")
-
 #define MAXIMAL_HW_CREDIT	254
 #define MINIMAL_HW_CREDIT	32
 
@@ -72,11 +68,26 @@ void BLEObject::serviceStateChanged(QLowEnergyService::ServiceState newState)
 			report_info("%s %d", to_str(service->serviceUuid()).c_str(), static_cast<int>(newState));
 }
 
+static bool is_hw(const device_data_t &d)
+{
+	return d.vendor == "Heinrichs Weikamp";
+}
+
+static bool is_shearwater(const device_data_t &d)
+{
+	return d.vendor == "Shearwater";
+}
+
+static bool is_garmin(const device_data_t &d)
+{
+	return d.vendor == "Garmin";
+}
+
 void BLEObject::characteristcStateChanged(const QLowEnergyCharacteristic &c, const QByteArray &value)
 {
 	if (verbose > 2 || debugCounter < DEBUG_THRESHOLD)
 		report_info("%s packet RECV %s", current_time().c_str(), qPrintable(value.toHex()));
-	if (IS_HW(device)) {
+	if (is_hw(device)) {
 		if (c.uuid() == telit[TELIT_DATA_TX] || c.uuid() == ublox[UBLOX_DATA_TX]) {
 			hw_credit--;
 			receivedPackets.append(value);
@@ -92,7 +103,7 @@ void BLEObject::characteristcStateChanged(const QLowEnergyCharacteristic &c, con
 
 void BLEObject::characteristicWritten(const QLowEnergyCharacteristic &c, const QByteArray &value)
 {
-	if (IS_HW(device)) {
+	if (is_hw(device)) {
 		if (c.uuid() == telit[TELIT_CREDITS_RX] || c.uuid() == ublox[UBLOX_CREDITS_RX]) {
 			bool ok;
 			hw_credit += value.toHex().toInt(&ok, 16);
@@ -246,13 +257,13 @@ void BLEObject::addService(const QBluetoothUuid &newService)
 	}
 }
 
-BLEObject::BLEObject(QLowEnergyController *c, device_data_t *d)
+BLEObject::BLEObject(QLowEnergyController *c, device_data_t &d) :
+	controller(c),
+	isCharacteristicWritten(false),
+	device(d),
+	timeout(BLE_TIMEOUT)
 {
-	controller = c;
-	device = d;
 	debugCounter = 0;
-	isCharacteristicWritten = false;
-	timeout = BLE_TIMEOUT;
 }
 
 BLEObject::~BLEObject()
@@ -549,9 +560,9 @@ dc_status_t BLEObject::setupHwTerminalIo(const QList<QLowEnergyCharacteristic> &
 // Bluez is broken, and doesn't have a sane way to query
 // whether to use a random address or not. So we have to
 // fake it.
-static int use_random_address(device_data_t *user_device)
+static int use_random_address(const device_data_t &user_device)
 {
-	return IS_SHEARWATER(user_device) || IS_GARMIN(user_device);
+	return is_shearwater(user_device) || is_garmin(user_device);
 }
 #endif
 
@@ -587,7 +598,7 @@ dc_status_t qt_ble_open(void **io, dc_context_t *, const char *devaddr, device_d
 	report_info("qt_ble_open(%s)", devaddr);
 
 #if !defined(Q_OS_WIN)
-	if (use_random_address(user_device))
+	if (use_random_address(*user_device))
 		controller->setRemoteAddressType(QLowEnergyController::RandomAddress);
 #endif
 
@@ -616,7 +627,7 @@ dc_status_t qt_ble_open(void **io, dc_context_t *, const char *devaddr, device_d
 	// We need to discover services etc here!
 	// Note that ble takes ownership of controller and henceforth deleting ble will
 	// take care of deleting controller.
-	BLEObject *ble = new BLEObject(controller, user_device);
+	BLEObject *ble = new BLEObject(controller, *user_device);
 	// we used to call our addService function the moment a service was discovered, but that
 	// could cause us to try to discover the details of a characteristic while we were still serching
 	// for services, which can cause a failure in the Qt BLE stack.
@@ -657,7 +668,7 @@ dc_status_t qt_ble_open(void **io, dc_context_t *, const char *devaddr, device_d
 
 	/* Enable notifications */
 	QList<QLowEnergyCharacteristic> list = ble->preferredService()->characteristics();
-	if (IS_HW(user_device)) {
+	if (is_hw(*user_device)) {
 		dc_status_t r = ble->setupHwTerminalIo(list);
 		if (r != DC_STATUS_SUCCESS) {
 			delete ble;
@@ -747,6 +758,14 @@ dc_status_t qt_ble_poll(void *io, int timeout)
 	return ble->poll(timeout);
 }
 
+dc_status_t BLEObject::get_name(char *data, size_t size)
+{
+	if (device.btname.empty())
+		return DC_STATUS_UNSUPPORTED;
+	strncpy(data, device.btname.c_str(), size);
+	return DC_STATUS_SUCCESS;
+}
+
 dc_status_t qt_ble_ioctl(void *io, unsigned int request, void *data, size_t size)
 {
 	BLEObject *ble = (BLEObject *) io;
diff --git a/core/qt-ble.h b/core/qt-ble.h
index b7e2cfcf7..e45f8de2e 100644
--- a/core/qt-ble.h
+++ b/core/qt-ble.h
@@ -23,17 +23,12 @@ class BLEObject : public QObject
 	Q_OBJECT
 
 public:
-	BLEObject(QLowEnergyController *c, device_data_t *);
+	BLEObject(QLowEnergyController *c, device_data_t &);
 	~BLEObject();
 	inline void set_timeout(int value) { timeout = value; }
 	dc_status_t write(const void* data, size_t size, size_t *actual);
 	dc_status_t read(void* data, size_t size, size_t *actual);
-	inline dc_status_t get_name(char *res, size_t size)
-	{
-		if (!device->btname) return DC_STATUS_UNSUPPORTED;
-		strncpy(res, device->btname, size);
-		return DC_STATUS_SUCCESS;
-	}
+	dc_status_t get_name(char *res, size_t size);
 	dc_status_t poll(int timeout);
 
 	inline QLowEnergyService *preferredService() { return preferred; }
@@ -51,11 +46,11 @@ public slots:
 private:
 	QVector<QLowEnergyService *> services;
 
-	QLowEnergyController *controller = nullptr;
+	QLowEnergyController *controller;
 	QLowEnergyService *preferred = nullptr;
 	QList<QByteArray> receivedPackets;
 	bool isCharacteristicWritten;
-	device_data_t *device;
+	device_data_t &device;
 	unsigned int hw_credit = 0;
 	unsigned int desc_written = 0;
 	int timeout;
diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp
index c1efa73bb..c6380ae76 100644
--- a/core/uemis-downloader.cpp
+++ b/core/uemis-downloader.cpp
@@ -474,7 +474,7 @@ static std::string build_ans_path(const std::string &path, int filenumber)
 }
 
 /* send a request to the dive computer and collect the answer */
-static std::string uemis_get_answer(const char *path, const std::string &request, int n_param_in,
+static std::string uemis_get_answer(const std::string &path, const std::string &request, int n_param_in,
 				   int n_param_out, std::string &error_text)
 {
 	int i = 0;
@@ -535,7 +535,7 @@ static std::string uemis_get_answer(const char *path, const std::string &request
 		if (import_thread_cancelled)
 			return std::string();
 		progress_bar_fraction = filenr / (double)uemis_max_files;
-		std::string ans_path = build_ans_path(std::string(path), filenr - 1);
+		std::string ans_path = build_ans_path(path, filenr - 1);
 		ans_file = subsurface_open(ans_path.c_str(), O_RDONLY, 0666);
 		if (ans_file < 0) {
 			error_text = "can't open Uemis response file";
@@ -1073,7 +1073,7 @@ static void do_delete_dives(struct dive_table *td, int idx)
 		td->dives[x]->hidden_by_filter = true;
 }
 
-static bool load_uemis_divespot(const char *mountpath, int divespot_id)
+static bool load_uemis_divespot(const std::string &mountpath, int divespot_id)
 {
 	param_buff[2] = std::to_string(divespot_id);
 #if UEMIS_DEBUG & 2
@@ -1090,7 +1090,7 @@ static bool load_uemis_divespot(const char *mountpath, int divespot_id)
 	return false;
 }
 
-static void get_uemis_divespot(device_data_t *devdata, const char *mountpath, int divespot_id, struct dive *dive)
+static void get_uemis_divespot(device_data_t *devdata, const std::string &mountpath, int divespot_id, struct dive *dive)
 {
 	struct dive_site *nds = dive->dive_site;
 
@@ -1128,7 +1128,7 @@ static void get_uemis_divespot(device_data_t *devdata, const char *mountpath, in
 	}
 }
 
-static bool get_matching_dive(int idx, int &newmax, uemis_mem_status &mem_status, device_data_t *data, const char *mountpath, const char deviceidnr)
+static bool get_matching_dive(int idx, int &newmax, uemis_mem_status &mem_status, device_data_t *data, const std::string &mountpath, const char deviceidnr)
 {
 	struct dive *dive = data->log->dives->dives[idx];
 	char log_file_no_to_find[20];
@@ -1228,7 +1228,7 @@ static bool get_matching_dive(int idx, int &newmax, uemis_mem_status &mem_status
 
 std::string do_uemis_import(device_data_t *data)
 {
-	const char *mountpath = data->devname;
+	const std::string &mountpath = data->devname;
 	short force_download = data->force_download;
 	int newmax = -1;
 	int first, start, end = -2;
diff --git a/desktop-widgets/configuredivecomputerdialog.cpp b/desktop-widgets/configuredivecomputerdialog.cpp
index b4a25f352..8012a6ed0 100644
--- a/desktop-widgets/configuredivecomputerdialog.cpp
+++ b/desktop-widgets/configuredivecomputerdialog.cpp
@@ -311,7 +311,7 @@ void OstcFirmwareCheck::checkLatest(QWidget *_parent, device_data_t *data, const
 	QStringList fwParts = latestFirmwareAvailable.split(".");
 	int latestFirmwareAvailableNumber;
 
-	if (strcmp(data->product, "OSTC 4") == 0) {
+	if (data->product == "OSTC 4") {
 		unsigned char X, Y, Z, beta;
 		X = (firmwareOnDevice & 0xF800) >> 11;
 		Y = (firmwareOnDevice & 0x07C0) >> 6;
@@ -1532,10 +1532,10 @@ void ConfigureDiveComputerDialog::dc_open()
 
 	ui.progressBar->setFormat(tr("Connected to device"));
 
-	qPrefDiveComputer::set_device(device_data.devname);
-	qPrefDiveComputer::set_device_name(device_data.btname);
-	qPrefDiveComputer::set_vendor(device_data.vendor);
-	qPrefDiveComputer::set_product(device_data.product);
+	qPrefDiveComputer::set_device(device_data.devname.c_str());
+	qPrefDiveComputer::set_device_name(device_data.btname.c_str());
+	qPrefDiveComputer::set_vendor(device_data.vendor.c_str());
+	qPrefDiveComputer::set_product(device_data.product.c_str());
 }
 
 void ConfigureDiveComputerDialog::dc_close()
diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp
index 027ddad96..ce8773ca9 100644
--- a/smtk-import/smartrak.cpp
+++ b/smtk-import/smartrak.cpp
@@ -824,7 +824,7 @@ static dc_status_t prepare_data(int data_model, const char *serial, dc_family_t
 	dev_data.device = NULL;
 	dev_data.context = NULL;
 	if (!data_model) {
-		dev_data.model = copy_string(manual_dc_name);
+		dev_data.model = manual_dc_name;
 		dev_data.descriptor = NULL;
 		return DC_STATUS_NODEVICE;
 	}
@@ -832,11 +832,11 @@ static dc_status_t prepare_data(int data_model, const char *serial, dc_family_t
 	if (dev_data.descriptor) {
 		dev_data.vendor = dc_descriptor_get_vendor(dev_data.descriptor);
 		dev_data.product = dc_descriptor_get_product(dev_data.descriptor);
-		concat(&dev_data.model, "", format_string_std("%s %s", dev_data.vendor, dev_data.product));
+		dev_data.model += dev_data.vendor + " " + dev_data.product;
 		dev_data.devinfo.serial = (uint32_t) lrint(strtod(serial, NULL));
 		return DC_STATUS_SUCCESS;
 	} else {
-		dev_data.model = copy_string("unsupported dive computer");
+		dev_data.model = "unsupported dive computer";
 		dev_data.devinfo.serial = (uint32_t) lrint(strtod(serial, NULL));
 		return DC_STATUS_UNSUPPORTED;
 	}
@@ -844,7 +844,6 @@ static dc_status_t prepare_data(int data_model, const char *serial, dc_family_t
 
 static void device_data_free(device_data_t &dev_data)
 {
-	free((void *) dev_data.model);
 	dc_descriptor_free(dev_data.descriptor);
 }
 
@@ -952,7 +951,7 @@ extern "C" void smartrak_import(const char *file, struct divelog *log)
 				dc_fam = DC_FAMILY_UWATEC_ALADIN;
 		}
 		rc = prepare_data(dc_model, (char *)col[coln(DCNUMBER)]->bind_ptr, dc_fam, devdata);
-		smtkdive->dc.model = copy_string(devdata.model);
+		smtkdive->dc.model = copy_string(devdata.model.c_str());
 		if (rc == DC_STATUS_SUCCESS && mdb_table.get_len(coln(PROFILE))) {
 			prf_buffer = static_cast<unsigned char *>(mdb_ole_read_full(mdb, col[coln(PROFILE)], &prf_length));
 			if (prf_length > 0) {