2017-11-27 17:41:10 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <assert.h>
|
2017-11-27 20:55:28 +00:00
|
|
|
#include <stdarg.h>
|
2017-11-27 17:41:10 +00:00
|
|
|
#include "membuffer.h"
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <libdivecomputer/parser.h>
|
|
|
|
|
2020-10-25 12:28:55 +00:00
|
|
|
#include "parse.h"
|
|
|
|
#include "dive.h"
|
2022-11-12 07:57:56 +00:00
|
|
|
#include "divelog.h"
|
2019-03-04 22:20:29 +00:00
|
|
|
#include "divesite.h"
|
2019-08-05 17:41:15 +00:00
|
|
|
#include "errorhelper.h"
|
2024-05-04 15:18:08 +00:00
|
|
|
#include "format.h"
|
2020-10-25 12:28:55 +00:00
|
|
|
#include "sample.h"
|
2018-05-11 15:25:41 +00:00
|
|
|
#include "subsurface-string.h"
|
2020-04-10 07:42:14 +00:00
|
|
|
#include "picture.h"
|
2019-05-31 14:09:14 +00:00
|
|
|
#include "trip.h"
|
2017-11-27 17:41:10 +00:00
|
|
|
#include "device.h"
|
2017-11-27 18:03:05 +00:00
|
|
|
#include "gettext.h"
|
|
|
|
|
2024-05-31 15:15:47 +00:00
|
|
|
parser_state::parser_state() = default;
|
|
|
|
parser_state::~parser_state() = default;
|
2017-11-27 17:41:10 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we don't have an explicit dive computer,
|
|
|
|
* we use the implicit one that every dive has..
|
|
|
|
*/
|
2024-02-29 06:56:27 +00:00
|
|
|
struct divecomputer *get_dc(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2024-05-27 15:09:48 +00:00
|
|
|
return state->cur_dc ?: &state->cur_dive->dcs[0];
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-05-04 16:45:55 +00:00
|
|
|
void start_match(const char *type, const char *name, char *buffer)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
|
|
|
if (verbose > 2)
|
|
|
|
printf("Matching %s '%s' (%s)\n",
|
|
|
|
type, name, buffer);
|
|
|
|
}
|
|
|
|
|
2024-05-04 16:45:55 +00:00
|
|
|
void nonmatch(const char *type, const char *name, char *buffer)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
|
|
|
if (verbose > 1)
|
|
|
|
printf("Unable to match %s '%s' (%s)\n",
|
|
|
|
type, name, buffer);
|
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void event_start(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2024-05-04 17:15:47 +00:00
|
|
|
state->cur_event = event();
|
2024-02-29 06:56:27 +00:00
|
|
|
state->event_active = true; /* Active */
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void event_end(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2018-10-17 16:45:22 +00:00
|
|
|
struct divecomputer *dc = get_dc(state);
|
|
|
|
if (state->cur_event.type == 123) {
|
2024-05-04 15:31:04 +00:00
|
|
|
struct picture pic;
|
2024-05-30 13:00:28 +00:00
|
|
|
pic.filename = state->cur_event.name;
|
2017-11-27 17:41:10 +00:00
|
|
|
/* theoretically this could fail - but we didn't support multi year offsets */
|
2020-04-11 15:41:56 +00:00
|
|
|
pic.offset.seconds = state->cur_event.time.seconds;
|
2024-05-30 13:00:28 +00:00
|
|
|
add_picture(state->cur_dive->pictures, std::move(pic));
|
2017-11-27 17:41:10 +00:00
|
|
|
} else {
|
|
|
|
/* At some point gas change events did not have any type. Thus we need to add
|
|
|
|
* one on import, if we encounter the type one missing.
|
|
|
|
*/
|
2024-05-04 20:17:07 +00:00
|
|
|
if (state->cur_event.type == 0 && state->cur_event.name == "gaschange")
|
2018-10-17 16:45:22 +00:00
|
|
|
state->cur_event.type = state->cur_event.value >> 16 > 0 ? SAMPLE_EVENT_GASCHANGE2 : SAMPLE_EVENT_GASCHANGE;
|
2024-05-25 06:16:57 +00:00
|
|
|
|
|
|
|
struct event ev(state->cur_event.time.seconds, state->cur_event.type, state->cur_event.flags,
|
|
|
|
state->cur_event.value, state->cur_event.name);
|
2017-11-27 17:41:10 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Older logs might mark the dive to be CCR by having an "SP change" event at time 0:00. Better
|
|
|
|
* to mark them being CCR on import so no need for special treatments elsewhere on the code.
|
|
|
|
*/
|
2024-05-25 06:16:57 +00:00
|
|
|
if (ev.time.seconds == 0 && ev.type == SAMPLE_EVENT_PO2 && ev.value && dc->divemode==OC)
|
2017-11-27 17:41:10 +00:00
|
|
|
dc->divemode = CCR;
|
|
|
|
|
2024-05-25 18:12:10 +00:00
|
|
|
if (ev.is_gaschange()) {
|
2017-11-27 17:41:10 +00:00
|
|
|
/* See try_to_fill_event() on why the filled-in index is one too big */
|
2024-05-25 06:16:57 +00:00
|
|
|
ev.gas.index = state->cur_event.gas.index-1;
|
2018-10-17 16:45:22 +00:00
|
|
|
if (state->cur_event.gas.mix.o2.permille || state->cur_event.gas.mix.he.permille)
|
2024-05-25 06:16:57 +00:00
|
|
|
ev.gas.mix = state->cur_event.gas.mix;
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
2024-05-25 06:16:57 +00:00
|
|
|
|
|
|
|
add_event_to_dc(dc, std::move(ev));
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
2024-02-29 06:56:27 +00:00
|
|
|
state->event_active = false; /* No longer active */
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* While in some formats file boundaries are dive boundaries, in many
|
|
|
|
* others (as for example in our native format) there are
|
|
|
|
* multiple dives per file, so there can be other events too that
|
|
|
|
* trigger a "new dive" marker and you may get some nesting due
|
|
|
|
* to that. Just ignore nesting levels.
|
|
|
|
* On the flipside it is possible that we start an XML file that ends
|
|
|
|
* up having no dives in it at all - don't create a bogus empty dive
|
|
|
|
* for those. It's not entirely clear what is the minimum set of data
|
|
|
|
* to make a dive valid, but if it has no location, no date and no
|
|
|
|
* samples I'm pretty sure it's useless.
|
|
|
|
*/
|
2024-02-29 06:56:27 +00:00
|
|
|
bool is_dive(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2018-10-17 16:45:22 +00:00
|
|
|
return state->cur_dive &&
|
2024-05-27 15:09:48 +00:00
|
|
|
(state->cur_dive->dive_site || state->cur_dive->when || !state->cur_dive->dcs[0].samples.empty());
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void reset_dc_info(struct divecomputer *, struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
|
|
|
/* WARN: reset dc info does't touch the dc? */
|
2018-10-17 16:45:22 +00:00
|
|
|
state->lastcylinderindex = 0;
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void reset_dc_settings(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2024-02-29 06:56:27 +00:00
|
|
|
state->cur_settings.dc.model.clear();
|
|
|
|
state->cur_settings.dc.nickname.clear();
|
|
|
|
state->cur_settings.dc.serial_nr.clear();
|
|
|
|
state->cur_settings.dc.firmware.clear();
|
2018-10-17 16:45:22 +00:00
|
|
|
state->cur_settings.dc.deviceid = 0;
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void reset_fingerprint(struct parser_state *state)
|
2021-10-30 23:31:29 +00:00
|
|
|
{
|
2024-02-29 06:56:27 +00:00
|
|
|
state->cur_settings.fingerprint.data.clear();
|
2021-10-30 23:31:29 +00:00
|
|
|
state->cur_settings.fingerprint.model = 0;
|
|
|
|
state->cur_settings.fingerprint.serial = 0;
|
|
|
|
state->cur_settings.fingerprint.fdeviceid = 0;
|
|
|
|
state->cur_settings.fingerprint.fdiveid = 0;
|
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void settings_start(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2018-10-17 16:45:22 +00:00
|
|
|
state->in_settings = true;
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void settings_end(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2018-10-17 16:45:22 +00:00
|
|
|
state->in_settings = false;
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void fingerprint_settings_start(struct parser_state *state)
|
2021-10-30 23:31:29 +00:00
|
|
|
{
|
|
|
|
reset_fingerprint(state);
|
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void fingerprint_settings_end(struct parser_state *state)
|
2021-10-30 23:31:29 +00:00
|
|
|
{
|
2024-05-31 06:22:30 +00:00
|
|
|
create_fingerprint_node_from_hex(*state->fingerprints,
|
2021-10-30 23:31:29 +00:00
|
|
|
state->cur_settings.fingerprint.model,
|
|
|
|
state->cur_settings.fingerprint.serial,
|
2024-05-31 06:22:30 +00:00
|
|
|
state->cur_settings.fingerprint.data,
|
2021-10-30 23:31:29 +00:00
|
|
|
state->cur_settings.fingerprint.fdeviceid,
|
|
|
|
state->cur_settings.fingerprint.fdiveid);
|
|
|
|
}
|
2024-02-28 21:01:51 +00:00
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void dc_settings_start(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2018-10-17 16:45:22 +00:00
|
|
|
reset_dc_settings(state);
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void dc_settings_end(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2024-05-31 05:08:54 +00:00
|
|
|
create_device_node(state->log->devices,
|
2024-05-18 15:03:19 +00:00
|
|
|
state->cur_settings.dc.model,
|
|
|
|
state->cur_settings.dc.serial_nr,
|
|
|
|
state->cur_settings.dc.nickname);
|
2018-10-17 16:45:22 +00:00
|
|
|
reset_dc_settings(state);
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void dive_site_start(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2018-10-17 16:45:22 +00:00
|
|
|
if (state->cur_dive_site)
|
2017-11-27 17:41:10 +00:00
|
|
|
return;
|
2020-09-06 10:39:51 +00:00
|
|
|
state->taxonomy_category = -1;
|
|
|
|
state->taxonomy_origin = -1;
|
2024-05-04 11:39:04 +00:00
|
|
|
state->cur_dive_site = std::make_unique<dive_site>();
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void dive_site_end(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2018-10-17 16:45:22 +00:00
|
|
|
if (!state->cur_dive_site)
|
2017-11-27 17:41:10 +00:00
|
|
|
return;
|
|
|
|
|
2024-05-11 12:22:33 +00:00
|
|
|
struct dive_site *ds = state->log->sites->alloc_or_get(state->cur_dive_site->uuid);
|
2024-05-11 13:01:37 +00:00
|
|
|
ds->merge(*state->cur_dive_site);
|
2022-08-29 05:25:21 +00:00
|
|
|
|
|
|
|
if (verbose > 3)
|
2024-05-04 15:18:08 +00:00
|
|
|
printf("completed dive site uuid %x8 name {%s}\n", ds->uuid, ds->name.c_str());
|
2022-08-29 05:25:21 +00:00
|
|
|
|
2024-05-04 11:39:04 +00:00
|
|
|
state->cur_dive_site.reset();
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void filter_preset_start(struct parser_state *state)
|
2020-06-17 20:45:33 +00:00
|
|
|
{
|
|
|
|
if (state->cur_filter)
|
|
|
|
return;
|
2024-03-01 21:44:45 +00:00
|
|
|
state->cur_filter = std::make_unique<filter_preset>();
|
2020-06-17 20:45:33 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void filter_preset_end(struct parser_state *state)
|
2020-06-17 20:45:33 +00:00
|
|
|
{
|
2024-05-13 17:34:43 +00:00
|
|
|
add_filter_preset_to_table(state->cur_filter.get(), state->log->filter_presets.get());
|
2024-03-01 21:44:45 +00:00
|
|
|
state->cur_filter.reset();
|
2020-06-17 20:45:33 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void fulltext_start(struct parser_state *state)
|
2020-06-17 20:45:33 +00:00
|
|
|
{
|
|
|
|
if (!state->cur_filter)
|
|
|
|
return;
|
|
|
|
state->in_fulltext = true;
|
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void fulltext_end(struct parser_state *state)
|
2020-06-17 20:45:33 +00:00
|
|
|
{
|
|
|
|
if (!state->in_fulltext)
|
|
|
|
return;
|
2024-03-01 21:44:45 +00:00
|
|
|
filter_preset_set_fulltext(state->cur_filter.get(), state->fulltext.c_str(), state->fulltext_string_mode.c_str());
|
2024-02-29 06:56:27 +00:00
|
|
|
state->fulltext.clear();
|
|
|
|
state->fulltext_string_mode.clear();
|
2020-06-17 20:45:33 +00:00
|
|
|
state->in_fulltext = false;
|
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void filter_constraint_start(struct parser_state *state)
|
2020-06-17 20:45:33 +00:00
|
|
|
{
|
|
|
|
if (!state->cur_filter)
|
|
|
|
return;
|
|
|
|
state->in_filter_constraint = true;
|
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void filter_constraint_end(struct parser_state *state)
|
2020-06-17 20:45:33 +00:00
|
|
|
{
|
|
|
|
if (!state->in_filter_constraint)
|
|
|
|
return;
|
2024-03-01 21:44:45 +00:00
|
|
|
filter_preset_add_constraint(state->cur_filter.get(), state->filter_constraint_type.c_str(), state->filter_constraint_string_mode.c_str(),
|
2024-02-29 06:56:27 +00:00
|
|
|
state->filter_constraint_range_mode.c_str(), state->filter_constraint_negate, state->filter_constraint.c_str());
|
|
|
|
|
|
|
|
state->filter_constraint_type.clear();
|
|
|
|
state->filter_constraint_string_mode.clear();
|
|
|
|
state->filter_constraint_range_mode.clear();
|
2020-06-17 20:45:33 +00:00
|
|
|
state->filter_constraint_negate = false;
|
2024-02-29 06:56:27 +00:00
|
|
|
state->filter_constraint.clear();
|
2020-06-17 20:45:33 +00:00
|
|
|
state->in_filter_constraint = false;
|
|
|
|
}
|
2017-11-27 17:41:10 +00:00
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void dive_start(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2018-10-17 16:45:22 +00:00
|
|
|
if (state->cur_dive)
|
2017-11-27 17:41:10 +00:00
|
|
|
return;
|
2024-05-16 18:11:21 +00:00
|
|
|
state->cur_dive = std::make_unique<dive>();
|
2024-05-27 15:09:48 +00:00
|
|
|
reset_dc_info(&state->cur_dive->dcs[0], state);
|
2018-10-17 16:45:22 +00:00
|
|
|
memset(&state->cur_tm, 0, sizeof(state->cur_tm));
|
|
|
|
state->o2pressure_sensor = 1;
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void dive_end(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2018-10-17 16:45:22 +00:00
|
|
|
if (!state->cur_dive)
|
2017-11-27 17:41:10 +00:00
|
|
|
return;
|
2024-05-16 18:11:21 +00:00
|
|
|
if (is_dive(state)) {
|
2019-10-26 19:16:02 +00:00
|
|
|
if (state->cur_trip)
|
2024-06-08 13:28:16 +00:00
|
|
|
state->cur_trip->add_dive(state->cur_dive.get());
|
2024-06-07 08:25:09 +00:00
|
|
|
// Note: we add dives in an unsorted way. The caller of the parsing
|
|
|
|
// function must sort dives.
|
|
|
|
fixup_dive(state->cur_dive.get());
|
|
|
|
state->log->dives.push_back(std::move(state->cur_dive));
|
|
|
|
// This would add dives in a sorted way:
|
|
|
|
// state->log->dives.record_dive(std::move(state->cur_dive));
|
2019-10-26 19:16:02 +00:00
|
|
|
}
|
2024-05-16 18:11:21 +00:00
|
|
|
state->cur_dive.reset();
|
2018-10-17 16:45:22 +00:00
|
|
|
state->cur_dc = NULL;
|
|
|
|
state->cur_location.lat.udeg = 0;
|
|
|
|
state->cur_location.lon.udeg = 0;
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void trip_start(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2018-10-17 16:45:22 +00:00
|
|
|
if (state->cur_trip)
|
2017-11-27 17:41:10 +00:00
|
|
|
return;
|
2018-10-17 16:45:22 +00:00
|
|
|
dive_end(state);
|
2024-05-31 15:15:47 +00:00
|
|
|
state->cur_trip = std::make_unique<dive_trip>();
|
2018-10-17 16:45:22 +00:00
|
|
|
memset(&state->cur_tm, 0, sizeof(state->cur_tm));
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void trip_end(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2018-10-17 16:45:22 +00:00
|
|
|
if (!state->cur_trip)
|
2017-11-27 17:41:10 +00:00
|
|
|
return;
|
2024-06-01 20:05:57 +00:00
|
|
|
state->log->trips->put(std::move(state->cur_trip));
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void picture_start(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void picture_end(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2024-05-30 13:00:28 +00:00
|
|
|
add_picture(state->cur_dive->pictures, std::move(state->cur_picture));
|
2020-04-11 15:41:56 +00:00
|
|
|
/* dive_add_picture took ownership, we can just clear out copy of the data */
|
2024-05-04 15:31:04 +00:00
|
|
|
state->cur_picture = picture();
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
cylinder_t *cylinder_start(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2019-08-04 17:17:11 +00:00
|
|
|
return add_empty_cylinder(&state->cur_dive->cylinders);
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void cylinder_end(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void ws_start(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2024-05-29 05:03:03 +00:00
|
|
|
state->cur_dive->weightsystems.emplace_back();
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void ws_end(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-07-18 10:33:31 +00:00
|
|
|
/*
|
|
|
|
* If the given cylinder doesn't exist, return NO_SENSOR.
|
|
|
|
*/
|
2022-09-20 16:33:07 +00:00
|
|
|
static int sanitize_sensor_id(const struct dive *d, int nr)
|
2021-07-18 10:33:31 +00:00
|
|
|
{
|
2024-05-28 19:31:11 +00:00
|
|
|
return d && nr >= 0 && static_cast<size_t>(nr) < d->cylinders.size() ? nr : NO_SENSOR;
|
2021-07-18 10:33:31 +00:00
|
|
|
}
|
|
|
|
|
2017-11-27 17:41:10 +00:00
|
|
|
/*
|
|
|
|
* By default the sample data does not change unless the
|
|
|
|
* save-file gives an explicit new value. So we copy the
|
|
|
|
* data from the previous sample if one exists, and then
|
|
|
|
* the parsing will update it as necessary.
|
|
|
|
*
|
|
|
|
* There are a few exceptions, like the sample pressure:
|
|
|
|
* missing sample pressure doesn't mean "same as last
|
|
|
|
* time", but "interpolate". We clear those ones
|
|
|
|
* explicitly.
|
|
|
|
*
|
|
|
|
* NOTE! We default sensor use to 0, 1 respetively for
|
|
|
|
* the two sensors, but for CCR dives with explicit
|
|
|
|
* OXYGEN bottles we set the secondary sensor to that.
|
|
|
|
* Then the primary sensor will be either the first
|
|
|
|
* or the second cylinder depending on what isn't an
|
|
|
|
* oxygen cylinder.
|
|
|
|
*/
|
2024-02-29 06:56:27 +00:00
|
|
|
void sample_start(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2018-10-17 16:45:22 +00:00
|
|
|
struct divecomputer *dc = get_dc(state);
|
2017-11-27 17:41:10 +00:00
|
|
|
struct sample *sample = prepare_sample(dc);
|
|
|
|
|
2024-05-19 10:38:38 +00:00
|
|
|
if (dc->samples.size() > 1) {
|
|
|
|
*sample = dc->samples[dc->samples.size() - 2];
|
2017-11-27 17:41:10 +00:00
|
|
|
sample->pressure[0].mbar = 0;
|
|
|
|
sample->pressure[1].mbar = 0;
|
|
|
|
} else {
|
2024-05-16 18:11:21 +00:00
|
|
|
sample->sensor[0] = sanitize_sensor_id(state->cur_dive.get(), !state->o2pressure_sensor);
|
|
|
|
sample->sensor[1] = sanitize_sensor_id(state->cur_dive.get(), state->o2pressure_sensor);
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
2018-10-17 16:45:22 +00:00
|
|
|
state->cur_sample = sample;
|
|
|
|
state->next_o2_sensor = 0;
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void sample_end(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2018-10-17 16:45:22 +00:00
|
|
|
if (!state->cur_dive)
|
2017-11-27 17:41:10 +00:00
|
|
|
return;
|
|
|
|
|
2018-10-17 16:45:22 +00:00
|
|
|
state->cur_sample = NULL;
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void divecomputer_start(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2024-05-27 15:09:48 +00:00
|
|
|
struct divecomputer *dc = &state->cur_dive->dcs.back();
|
2017-11-27 17:41:10 +00:00
|
|
|
|
|
|
|
/* Did we already fill that in? */
|
2024-05-19 10:38:38 +00:00
|
|
|
if (!dc->samples.empty() || !dc->model.empty() || dc->when) {
|
2024-05-27 15:09:48 +00:00
|
|
|
state->cur_dive->dcs.emplace_back();
|
|
|
|
dc = &state->cur_dive->dcs.back();
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* .. this is the one we'll use */
|
2018-10-17 16:45:22 +00:00
|
|
|
state->cur_dc = dc;
|
|
|
|
reset_dc_info(dc, state);
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void divecomputer_end(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2018-10-17 16:45:22 +00:00
|
|
|
if (!state->cur_dc->when)
|
|
|
|
state->cur_dc->when = state->cur_dive->when;
|
|
|
|
state->cur_dc = NULL;
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void userid_start(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2018-10-17 16:45:22 +00:00
|
|
|
state->in_userid = true;
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void userid_stop(struct parser_state *state)
|
2017-11-27 17:41:10 +00:00
|
|
|
{
|
2018-10-17 16:45:22 +00:00
|
|
|
state->in_userid = false;
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2018-10-17 18:22:17 +00:00
|
|
|
/*
|
2024-05-31 15:23:29 +00:00
|
|
|
* Copy whitespace-trimmed string.
|
2018-10-17 18:22:17 +00:00
|
|
|
*/
|
2024-02-28 21:01:51 +00:00
|
|
|
void utf8_string_std(const char *buffer, std::string *res)
|
|
|
|
{
|
|
|
|
while (isspace(*buffer))
|
|
|
|
++buffer;
|
|
|
|
if (!*buffer) {
|
|
|
|
res->clear();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const char *end = buffer + strlen(buffer);
|
|
|
|
while (isspace(end[-1]))
|
|
|
|
--end;
|
|
|
|
*res = std::string(buffer, end - buffer);
|
2017-11-27 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:27 +00:00
|
|
|
void add_dive_site(const char *ds_name, struct dive *dive, struct parser_state *state)
|
2017-11-27 18:01:19 +00:00
|
|
|
{
|
2024-02-28 21:01:51 +00:00
|
|
|
const char *buffer = ds_name;
|
|
|
|
std::string trimmed = trimspace(buffer);
|
|
|
|
if (!trimmed.empty()) {
|
2018-10-26 15:03:54 +00:00
|
|
|
struct dive_site *ds = dive->dive_site;
|
2018-10-23 10:42:01 +00:00
|
|
|
if (!ds) {
|
2019-03-05 21:58:47 +00:00
|
|
|
// if the dive doesn't have a dive site, check if there's already a dive site by this name
|
2024-05-11 12:22:33 +00:00
|
|
|
ds = state->log->sites->get_by_name(trimmed);
|
2017-11-27 18:01:19 +00:00
|
|
|
}
|
|
|
|
if (ds) {
|
2019-03-05 21:58:47 +00:00
|
|
|
// we have a dive site, let's hope there isn't a different name
|
2024-05-04 15:18:08 +00:00
|
|
|
if (ds->name.empty()) {
|
|
|
|
ds->name = trimmed;
|
2024-02-28 21:01:51 +00:00
|
|
|
} else if (trimmed != ds->name) {
|
2017-11-27 18:01:19 +00:00
|
|
|
// if it's not the same name, it's not the same dive site
|
|
|
|
// but wait, we could have gotten this one based on GPS coords and could
|
|
|
|
// have had two different names for the same site... so let's search the other
|
|
|
|
// way around
|
2024-05-12 20:30:15 +00:00
|
|
|
struct dive_site *exact_match = state->log->sites->get_by_gps_and_name(trimmed, ds->location);
|
2018-10-23 10:42:01 +00:00
|
|
|
if (exact_match) {
|
2019-03-05 21:58:47 +00:00
|
|
|
unregister_dive_from_dive_site(dive);
|
2024-05-11 13:01:37 +00:00
|
|
|
exact_match->add_dive(dive);
|
2017-11-27 18:01:19 +00:00
|
|
|
} else {
|
2024-05-11 12:22:33 +00:00
|
|
|
struct dive_site *newds = state->log->sites->create(trimmed.c_str());
|
2019-03-05 21:58:47 +00:00
|
|
|
unregister_dive_from_dive_site(dive);
|
2024-05-11 13:01:37 +00:00
|
|
|
newds->add_dive(dive);
|
2018-10-17 16:45:22 +00:00
|
|
|
if (has_location(&state->cur_location)) {
|
2017-11-27 18:01:19 +00:00
|
|
|
// we started this uuid with GPS data, so lets use those
|
2018-10-17 16:45:22 +00:00
|
|
|
newds->location = state->cur_location;
|
2017-11-27 18:01:19 +00:00
|
|
|
} else {
|
2018-10-20 18:12:15 +00:00
|
|
|
newds->location = ds->location;
|
2017-11-27 18:01:19 +00:00
|
|
|
}
|
2024-05-04 15:18:08 +00:00
|
|
|
newds->notes += '\n';
|
|
|
|
newds->notes += format_string_std(translate("gettextFromC", "additional name for site: %s\n"), ds->name.c_str());
|
2017-11-27 18:01:19 +00:00
|
|
|
}
|
2019-03-05 21:58:47 +00:00
|
|
|
} else if (dive->dive_site != ds) {
|
2017-11-27 18:01:19 +00:00
|
|
|
// add the existing dive site to the current dive
|
2019-03-05 21:58:47 +00:00
|
|
|
unregister_dive_from_dive_site(dive);
|
2024-05-11 13:01:37 +00:00
|
|
|
ds->add_dive(dive);
|
2017-11-27 18:01:19 +00:00
|
|
|
}
|
|
|
|
} else {
|
2024-05-11 13:01:37 +00:00
|
|
|
state->log->sites->create(trimmed)->add_dive(dive);
|
2017-11-27 18:01:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-04 16:45:55 +00:00
|
|
|
int atoi_n(char *ptr, unsigned int len)
|
2017-11-27 18:28:41 +00:00
|
|
|
{
|
|
|
|
if (len < 10) {
|
|
|
|
char buf[10];
|
|
|
|
|
|
|
|
memcpy(buf, ptr, len);
|
|
|
|
buf[len] = 0;
|
|
|
|
return atoi(buf);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|