diff --git a/core/format.cpp b/core/format.cpp index 693a8f7f2..dc9d3db0f 100644 --- a/core/format.cpp +++ b/core/format.cpp @@ -353,20 +353,27 @@ std::string casprintf_loc(const char *cformat, ...) return std::string(utf8.constData(), utf8.size()); } -std::string __printf(1, 2) format_string_std(const char *fmt, ...) +std::string format_string_std(const char *fmt, ...) { va_list ap; va_start(ap, fmt); - size_t stringsize = vsnprintf(NULL, 0, fmt, ap); + std::string res = vformat_string_std(fmt, ap); va_end(ap); + return res; +} + +std::string vformat_string_std(const char *fmt, va_list ap) +{ + va_list ap2; + va_copy(ap2, ap); + size_t stringsize = vsnprintf(NULL, 0, fmt, ap2); + va_end(ap2); if (stringsize == 0) return std::string(); std::string res; res.resize(stringsize); // Pointless clearing, oh my. // This overwrites the terminal null-byte of std::string. // That's probably "undefined behavior". Oh my. - va_start(ap, fmt); vsnprintf(res.data(), stringsize + 1, fmt, ap); - va_end(ap); return res; } diff --git a/core/format.h b/core/format.h index 0a03eea96..9b9567f5d 100644 --- a/core/format.h +++ b/core/format.h @@ -11,6 +11,7 @@ __printf(1, 2) QString qasprintf_loc(const char *cformat, ...); __printf(1, 0) QString vqasprintf_loc(const char *cformat, va_list ap); __printf(1, 2) std::string casprintf_loc(const char *cformat, ...); +__printf(1, 0) std::string vformat_string_std(const char *fmt, va_list ap); __printf(1, 2) std::string format_string_std(const char *fmt, ...); #endif diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 5ab8c5920..8804cc3fc 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -43,8 +43,8 @@ std::string dumpfile_name; std::string logfile_name; -const char *progress_bar_text = ""; -void (*progress_callback)(const char *text) = NULL; +std::string progress_bar_text; +void (*progress_callback)(const std::string &text) = NULL; double progress_bar_fraction = 0.0; static int stoptime, stopdepth, ndl, po2, cns, heartbeat, bearing; @@ -491,33 +491,30 @@ sample_cb(dc_sample_type_t type, const dc_sample_value_t *pvalue, void *userdata } } -static void dev_info(device_data_t *, const char *fmt, ...) +static void dev_info(const char *fmt, ...) { - static char buffer[1024]; va_list ap; va_start(ap, fmt); - vsnprintf(buffer, sizeof(buffer), fmt, ap); + progress_bar_text = vformat_string_std(fmt, ap); va_end(ap); - progress_bar_text = buffer; if (verbose) - INFO("dev_info: %s", buffer); + INFO("dev_info: %s", progress_bar_text.c_str()); if (progress_callback) - (*progress_callback)(buffer); + (*progress_callback)(progress_bar_text); } static int import_dive_number = 0; static void download_error(const char *fmt, ...) { - static char buffer[1024]; va_list ap; va_start(ap, fmt); - vsnprintf(buffer, sizeof(buffer), fmt, ap); + std::string buffer = vformat_string_std(fmt, ap); va_end(ap); - report_error("Dive %d: %s", import_dive_number, buffer); + report_error("Dive %d: %s", import_dive_number, buffer.c_str()); } static dc_status_t parse_samples(device_data_t *, struct divecomputer *dc, dc_parser_t *parser) @@ -649,7 +646,7 @@ static dc_status_t libdc_header_parser(dc_parser_t *parser, device_data_t *devda // Parse the divetime. std::string date_string = get_dive_date_c_string(dive->when); - dev_info(devdata, translate("gettextFromC", "Dive %d: %s"), import_dive_number, date_string.c_str()); + dev_info(translate("gettextFromC", "Dive %d: %s"), import_dive_number, date_string.c_str()); unsigned int divetime = 0; rc = dc_parser_get_field(parser, DC_FIELD_DIVETIME, 0, &divetime); @@ -846,7 +843,7 @@ static int dive_cb(const unsigned char *data, unsigned int size, /* If we already saw this dive, abort. */ if (!devdata->force_download && find_dive(dive->dcs[0])) { std::string date_string = get_dive_date_c_string(dive->when); - dev_info(devdata, translate("gettextFromC", "Already downloaded dive at %s"), date_string.c_str()); + dev_info(translate("gettextFromC", "Already downloaded dive at %s"), date_string.c_str()); return false; } @@ -884,7 +881,7 @@ static void do_save_fingerprint(device_data_t *devdata, const char *tmp, const c return; if (verbose) - dev_info(devdata, "Saving fingerprint for %08x:%08x to '%s'", + dev_info("Saving fingerprint for %08x:%08x to '%s'", devdata->fdeviceid, devdata->fdiveid, final); /* The fingerprint itself.. */ @@ -984,16 +981,16 @@ static void verify_fingerprint(dc_device_t *device, device_data_t *devdata, cons memcpy(&diveid, buffer + size + 4, 4); if (verbose) - dev_info(devdata, " ... fingerprinted dive %08x:%08x", deviceid, diveid); + dev_info(" ... fingerprinted dive %08x:%08x", deviceid, diveid); /* Only use it if we *have* that dive! */ if (!has_dive(deviceid, diveid)) { if (verbose) - dev_info(devdata, " ... dive not found"); + dev_info(" ... dive not found"); return; } dc_device_set_fingerprint(device, buffer, size); if (verbose) - dev_info(devdata, " ... fingerprint of size %zu", size); + dev_info(" ... fingerprint of size %zu", size); } /* @@ -1010,18 +1007,18 @@ static void lookup_fingerprint(dc_device_t *device, device_data_t *devdata) auto [fsize, raw_data] = get_fingerprint_data(fingerprints, calculate_string_hash(devdata->model.c_str()), devdata->devinfo.serial); if (fsize) { if (verbose) - dev_info(devdata, "... found fingerprint in dive table"); + dev_info("... found fingerprint in dive table"); dc_device_set_fingerprint(device, raw_data, fsize); return; } /* now check if we have a fingerprint on disk */ std::string cachename = fingerprint_file(devdata); if (verbose) - dev_info(devdata, "Looking for fingerprint in '%s'", cachename.c_str()); + dev_info("Looking for fingerprint in '%s'", cachename.c_str()); auto [mem, err] = readfile(cachename.c_str()); if (err > 0) { if (verbose) - dev_info(devdata, " ... got %zu bytes", mem.size()); + dev_info(" ... got %zu bytes", mem.size()); verify_fingerprint(device, devdata, (unsigned char *)mem.data(), mem.size()); } } @@ -1037,7 +1034,7 @@ static void event_cb(dc_device_t *device, dc_event_type_t event, const void *dat switch (event) { case DC_EVENT_WAITING: - dev_info(devdata, translate("gettextFromC", "Event: waiting for user action")); + dev_info(translate("gettextFromC", "Event: waiting for user action")); break; case DC_EVENT_PROGRESS: /* this seems really dumb... but having no idea what is happening on long @@ -1049,7 +1046,7 @@ static void event_cb(dc_device_t *device, dc_event_type_t event, const void *dat last = progress->current; if (progress->current > last + 10240) { last = progress->current; - dev_info(NULL, translate("gettextFromC", "read %dkb"), progress->current / 1024); + dev_info(translate("gettextFromC", "read %dkb"), progress->current / 1024); } if (progress->maximum) progress_bar_fraction = (double)progress->current / (double)progress->maximum; @@ -1069,7 +1066,7 @@ static void event_cb(dc_device_t *device, dc_event_type_t event, const void *dat devinfo->model, dc_descriptor_get_model(devdata->descriptor)); } } - dev_info(devdata, translate("gettextFromC", "model=%s firmware=%u serial=%u"), + dev_info(translate("gettextFromC", "model=%s firmware=%u serial=%u"), 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", @@ -1083,7 +1080,7 @@ static void event_cb(dc_device_t *device, dc_event_type_t event, const void *dat break; case DC_EVENT_CLOCK: - dev_info(devdata, translate("gettextFromC", "Event: systime=%" PRId64 ", devtime=%u\n"), + dev_info(translate("gettextFromC", "Event: systime=%" PRId64 ", devtime=%u\n"), (uint64_t)clock->systime, clock->devtime); if (devdata->libdc_logfile) { fprintf(devdata->libdc_logfile, "Event: systime=%" PRId64 ", devtime=%u\n", @@ -1121,7 +1118,7 @@ static std::string do_device_import(device_data_t *data) int events = DC_EVENT_WAITING | DC_EVENT_PROGRESS | DC_EVENT_DEVINFO | DC_EVENT_CLOCK | DC_EVENT_VENDOR; rc = dc_device_set_events(device, events, event_cb, data); if (rc != DC_STATUS_SUCCESS) { - dev_info(data, "Import error: %s", errmsg(rc)); + dev_info("Import error: %s", errmsg(rc)); return translate("gettextFromC", "Error registering the event handler."); } @@ -1129,7 +1126,7 @@ static std::string do_device_import(device_data_t *data) // Register the cancellation handler. rc = dc_device_set_cancel(device, cancel_cb, data); if (rc != DC_STATUS_SUCCESS) { - dev_info(data, "Import error: %s", errmsg(rc)); + dev_info("Import error: %s", errmsg(rc)); return translate("gettextFromC", "Error registering the cancellation handler."); } @@ -1154,7 +1151,7 @@ static std::string do_device_import(device_data_t *data) if (rc == DC_STATUS_UNSUPPORTED) return translate("gettextFromC", "Dumping not supported on this device"); - dev_info(data, "Import error: %s", errmsg(rc)); + dev_info("Import error: %s", errmsg(rc)); return translate("gettextFromC", "Dive data dumping error"); } @@ -1164,7 +1161,7 @@ static std::string do_device_import(device_data_t *data) if (rc != DC_STATUS_SUCCESS) { progress_bar_fraction = 0.0; - dev_info(data, "Import error: %s", errmsg(rc)); + dev_info("Import error: %s", errmsg(rc)); return translate("gettextFromC", "Dive data import error"); } @@ -1254,7 +1251,7 @@ static dc_status_t usbhid_device_open(dc_iostream_t **iostream, dc_context_t *co ERROR("didn't find HID device"); return DC_STATUS_NODEVICE; } - dev_info(data, "Opening USB HID device for %04x:%04x", + dev_info("Opening USB HID device for %04x:%04x", dc_usbhid_device_get_vid(device), dc_usbhid_device_get_pid(device)); rc = dc_usbhid_open(iostream, context, device); @@ -1277,7 +1274,7 @@ static dc_status_t usb_device_open(dc_iostream_t **iostream, dc_context_t *conte if (!device) return DC_STATUS_NODEVICE; - dev_info(data, "Opening USB device for %04x:%04x", + dev_info("Opening USB device for %04x:%04x", dc_usb_device_get_vid(device), dc_usb_device_get_pid(device)); rc = dc_usb_open(iostream, context, device); @@ -1305,7 +1302,7 @@ static dc_status_t irda_device_open(dc_iostream_t **iostream, dc_context_t *cont if (!address) std::from_chars(data->devname.c_str(), data->devname.c_str() + data->devname.size(), address); - dev_info(data, "Opening IRDA address %u", address); + dev_info("Opening IRDA address %u", address); return dc_irda_open(&data->iostream, context, address, 1); } @@ -1326,11 +1323,11 @@ static dc_status_t bluetooth_device_open(dc_context_t *context, device_data_t *d dc_iterator_free (iterator); if (!address) { - dev_info(data, "No rfcomm device found"); + dev_info("No rfcomm device found"); return DC_STATUS_NODEVICE; } - dev_info(data, "Opening rfcomm address %llu", address); + dev_info("Opening rfcomm address %llu", address); return dc_bluetooth_open(&data->iostream, context, address, 0); } #endif @@ -1346,13 +1343,13 @@ dc_status_t divecomputer_device_open(device_data_t *data) transports &= supported; if (!transports) { - dev_info(data, "Dive computer transport not supported"); + dev_info("Dive computer transport not supported"); return DC_STATUS_UNSUPPORTED; } #ifdef BT_SUPPORT if (transports & DC_TRANSPORT_BLUETOOTH) { - dev_info(data, "Opening rfcomm stream %s", data->devname.c_str()); + dev_info("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.c_str()); @@ -1366,7 +1363,7 @@ 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.c_str()); + dev_info("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; @@ -1374,21 +1371,21 @@ dc_status_t divecomputer_device_open(device_data_t *data) #endif if (transports & DC_TRANSPORT_USBHID) { - dev_info(data, "Connecting to USB HID device"); + dev_info("Connecting to USB HID device"); rc = usbhid_device_open(&data->iostream, context, data); if (rc == DC_STATUS_SUCCESS) return rc; } if (transports & DC_TRANSPORT_USB) { - dev_info(data, "Connecting to native USB device"); + dev_info("Connecting to native USB device"); rc = usb_device_open(&data->iostream, context, data); if (rc == DC_STATUS_SUCCESS) return rc; } if (transports & DC_TRANSPORT_SERIAL) { - dev_info(data, "Opening serial device %s", data->devname.c_str()); + dev_info("Opening serial device %s", data->devname.c_str()); #ifdef SERIAL_FTDI if (!strcasecmp(data->devname.c_str(), "ftdi")) return ftdi_open(&data->iostream, context); @@ -1404,14 +1401,14 @@ dc_status_t divecomputer_device_open(device_data_t *data) } if (transports & DC_TRANSPORT_IRDA) { - dev_info(data, "Connecting to IRDA device"); + dev_info("Connecting to IRDA device"); rc = irda_device_open(&data->iostream, context, data); if (rc == DC_STATUS_SUCCESS) return rc; } if (transports & DC_TRANSPORT_USBSTORAGE) { - dev_info(data, "Opening USB storage at %s", data->devname.c_str()); + dev_info("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; @@ -1462,9 +1459,9 @@ std::string do_libdivecomputer_import(device_data_t *data) rc = divecomputer_device_open(data); if (rc != DC_STATUS_SUCCESS) { - dev_info(data, "Import error: %s", errmsg(rc)); + dev_info("Import error: %s", errmsg(rc)); } else { - dev_info(data, "Connecting ..."); + dev_info("Connecting ..."); 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); @@ -1475,26 +1472,26 @@ std::string do_libdivecomputer_import(device_data_t *data) err = translate("gettextFromC", "Error opening the device %s %s (%s).\nIn most cases, in order to debug this issue, a libdivecomputer logfile will be useful.\nYou can create this logfile by selecting the corresponding checkbox in the download dialog."); #endif } else { - dev_info(data, "Starting import ..."); + dev_info("Starting import ..."); err = do_device_import(data); /* TODO: Show the logfile to the user on error. */ - dev_info(data, "Import complete"); + dev_info("Import complete"); if (err.empty() && data->sync_time) { - dev_info(data, "Syncing dive computer time ..."); + dev_info("Syncing dive computer time ..."); rc = sync_divecomputer_time(data->device); switch (rc) { case DC_STATUS_SUCCESS: - dev_info(data, "Time sync complete"); + dev_info("Time sync complete"); break; case DC_STATUS_UNSUPPORTED: - dev_info(data, "Time sync not supported by dive computer"); + dev_info("Time sync not supported by dive computer"); break; default: - dev_info(data, "Time sync failed"); + dev_info("Time sync failed"); break; } @@ -1503,7 +1500,7 @@ std::string do_libdivecomputer_import(device_data_t *data) dc_device_close(data->device); data->device = NULL; if (data->log->dives.empty()) - dev_info(data, translate("gettextFromC", "No new dives downloaded from dive computer")); + dev_info(translate("gettextFromC", "No new dives downloaded from dive computer")); } dc_iostream_close(data->iostream); data->iostream = NULL; diff --git a/core/libdivecomputer.h b/core/libdivecomputer.h index afb8ebc91..b99350900 100644 --- a/core/libdivecomputer.h +++ b/core/libdivecomputer.h @@ -55,8 +55,8 @@ void logfunc(dc_context_t *context, dc_loglevel_t loglevel, const char *file, un dc_descriptor_t *get_descriptor(dc_family_t type, unsigned int model); extern int import_thread_cancelled; -extern const char *progress_bar_text; -extern void (*progress_callback)(const char *text); +extern std::string progress_bar_text; +extern void (*progress_callback)(const std::string &text); extern double progress_bar_fraction; dc_status_t ble_packet_open(dc_iostream_t **iostream, dc_context_t *context, const char* devaddr, void *userdata); diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index 1b9210148..84fa8406d 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -200,15 +200,13 @@ static struct dive *get_dive_by_uemis_diveid(device_data_t *devdata, uint32_t ob /* send text to the importer progress bar */ static void uemis_info(const char *fmt, ...) { - static char buffer[256]; va_list ap; va_start(ap, fmt); - vsnprintf(buffer, sizeof(buffer), fmt, ap); + progress_bar_text = vformat_string_std(fmt, ap); va_end(ap); - progress_bar_text = buffer; if (verbose) - report_info("Uemis downloader: %s", buffer); + report_info("Uemis downloader: %s", progress_bar_text.c_str()); } static long bytes_available(int file) diff --git a/desktop-widgets/downloadfromdivecomputer.cpp b/desktop-widgets/downloadfromdivecomputer.cpp index 86978ecea..eef892c4d 100644 --- a/desktop-widgets/downloadfromdivecomputer.cpp +++ b/desktop-widgets/downloadfromdivecomputer.cpp @@ -63,7 +63,7 @@ DownloadFromDCWidget::DownloadFromDCWidget(const QString &filename, QWidget *par ui.product->setModel(&productModel); ui.syncDiveComputerTime->setChecked(prefs.sync_dc_time); - progress_bar_text = ""; + progress_bar_text.clear(); timer->setInterval(200); @@ -212,21 +212,19 @@ DELETEDCBUTTON(4) void DownloadFromDCWidget::updateProgressBar() { - static char *last_text = NULL; - - if (empty_string(last_text)) { + if (last_text.empty()) { // if we get the first actual text after the download is finished // (which happens for example on the OSTC), then don't bother - if (!empty_string(progress_bar_text) && nearly_equal(progress_bar_fraction, 1.0)) - progress_bar_text = ""; + if (!progress_bar_text.empty() && nearly_equal(progress_bar_fraction, 1.0)) + progress_bar_text.clear(); } - if (!empty_string(progress_bar_text)) { + if (!progress_bar_text.empty()) { // once the progress bar text is set, setup the maximum so the user sees actual progress - ui.progressBar->setFormat(progress_bar_text); + ui.progressBar->setFormat(QString::fromStdString(progress_bar_text)); ui.progressBar->setMaximum(100); #if defined(Q_OS_MAC) // on mac the progress bar doesn't show its text - ui.progressText->setText(progress_bar_text); + ui.progressText->setText(QString::fromStdString(progress_bar_text)); #endif } else { if (nearly_0(progress_bar_fraction)) { @@ -248,8 +246,7 @@ void DownloadFromDCWidget::updateProgressBar() } } ui.progressBar->setValue(lrint(progress_bar_fraction * 100)); - free(last_text); - last_text = strdup(progress_bar_text); + last_text = progress_bar_text; } void DownloadFromDCWidget::updateState(states state) @@ -262,10 +259,10 @@ void DownloadFromDCWidget::updateState(states state) ui.progressBar->hide(); markChildrenAsEnabled(); timer->stop(); - progress_bar_text = ""; + progress_bar_text.clear(); #if defined(Q_OS_MAC) // on mac we show the text in a label - ui.progressText->setText(progress_bar_text); + ui.progressText->setText(QString::fromStdString(progress_bar_text)); #endif } @@ -288,10 +285,10 @@ void DownloadFromDCWidget::updateState(states state) ui.progressBar->setValue(0); ui.progressBar->hide(); markChildrenAsEnabled(); - progress_bar_text = ""; + progress_bar_text.clear(); #if defined(Q_OS_MAC) // on mac we show the text in a label - ui.progressText->setText(progress_bar_text); + ui.progressText->setText(QString::fromStdString(progress_bar_text)); #endif } @@ -300,19 +297,19 @@ void DownloadFromDCWidget::updateState(states state) // If we find an error, offer to retry, otherwise continue the interaction to pick the dives the user wants else if (currentState == DOWNLOADING && state == DONE) { timer->stop(); - if (QString(progress_bar_text).contains("error", Qt::CaseInsensitive)) { + if (QString::fromStdString(progress_bar_text).contains("error", Qt::CaseInsensitive)) { updateProgressBar(); markChildrenAsEnabled(); - progress_bar_text = ""; + progress_bar_text.clear(); } else { if (diveImportedModel->numDives() != 0) - progress_bar_text = ""; + progress_bar_text.clear(); ui.progressBar->setValue(100); markChildrenAsEnabled(); } #if defined(Q_OS_MAC) // on mac we show the text in a label - ui.progressText->setText(progress_bar_text); + ui.progressText->setText(QString::fromStdString(progress_bar_text)); #endif } @@ -333,11 +330,11 @@ void DownloadFromDCWidget::updateState(states state) QMessageBox::critical(this, TITLE_OR_TEXT(tr("Error"), QString::fromStdString(diveImportedModel->thread.error)), QMessageBox::Ok); markChildrenAsEnabled(); - progress_bar_text = ""; + progress_bar_text.clear(); ui.progressBar->hide(); #if defined(Q_OS_MAC) // on mac we show the text in a label - ui.progressText->setText(progress_bar_text); + ui.progressText->setText(QString::fromStdString(progress_bar_text)); #endif } diff --git a/desktop-widgets/downloadfromdivecomputer.h b/desktop-widgets/downloadfromdivecomputer.h index b85a6334f..a6a0a321f 100644 --- a/desktop-widgets/downloadfromdivecomputer.h +++ b/desktop-widgets/downloadfromdivecomputer.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "core/libdivecomputer.h" #include "desktop-widgets/configuredivecomputerdialog.h" @@ -77,6 +78,7 @@ private: QStringListModel productModel; Ui::DownloadFromDiveComputer ui; QString filename; + std::string last_text; bool downloading; int previousLast; diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index a22b2c611..09680a098 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -76,12 +76,13 @@ void showErrorFromC(char *buf) } // this gets called from libdivecomputer -static void progressCallback(const char *text) +static void progressCallback(const std::string &text) { QMLManager *self = QMLManager::instance(); if (self) { - self->appendTextToLog(QString(text)); - self->setProgressMessage(QString(text)); + QString s = QString::fromStdString(text); + self->appendTextToLog(s); + self->setProgressMessage(s); } }