mirror of
https://github.com/subsurface/subsurface.git
synced 2025-02-19 22:16:15 +00:00
core: convert parse-xml.c and parse.c to C++
This was very annoying, because the old code was not const-clean at all and trampled all over buffers. This makes the new code pretty messy for now. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
parent
5285243a40
commit
3a130fe242
9 changed files with 216 additions and 207 deletions
555
core/parse.cpp
Normal file
555
core/parse.cpp
Normal file
|
@ -0,0 +1,555 @@
|
|||
#include "ssrf.h"
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include "membuffer.h"
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <libdivecomputer/parser.h>
|
||||
|
||||
#include "parse.h"
|
||||
#include "dive.h"
|
||||
#include "divelog.h"
|
||||
#include "divesite.h"
|
||||
#include "errorhelper.h"
|
||||
#include "sample.h"
|
||||
#include "subsurface-string.h"
|
||||
#include "picture.h"
|
||||
#include "trip.h"
|
||||
#include "device.h"
|
||||
#include "gettext.h"
|
||||
|
||||
extern "C" void init_parser_state(struct parser_state *state)
|
||||
{
|
||||
memset(state, 0, sizeof(*state));
|
||||
state->metric = true;
|
||||
state->cur_event.deleted = 1;
|
||||
state->sample_rate = 0;
|
||||
}
|
||||
|
||||
extern "C" void free_parser_state(struct parser_state *state)
|
||||
{
|
||||
free_dive(state->cur_dive);
|
||||
free_trip(state->cur_trip);
|
||||
free_dive_site(state->cur_dive_site);
|
||||
free_filter_preset(state->cur_filter);
|
||||
free((void *)state->cur_extra_data.key);
|
||||
free((void *)state->cur_extra_data.value);
|
||||
free((void *)state->cur_settings.dc.model);
|
||||
free((void *)state->cur_settings.dc.nickname);
|
||||
free((void *)state->cur_settings.dc.serial_nr);
|
||||
free((void *)state->cur_settings.dc.firmware);
|
||||
free(state->country);
|
||||
free(state->city);
|
||||
free(state->fulltext);
|
||||
free(state->fulltext_string_mode);
|
||||
free(state->filter_constraint_type);
|
||||
free(state->filter_constraint_string_mode);
|
||||
free(state->filter_constraint_range_mode);
|
||||
free(state->filter_constraint);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we don't have an explicit dive computer,
|
||||
* we use the implicit one that every dive has..
|
||||
*/
|
||||
extern "C" struct divecomputer *get_dc(struct parser_state *state)
|
||||
{
|
||||
return state->cur_dc ?: &state->cur_dive->dc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a dive into the dive_table array
|
||||
*/
|
||||
extern "C" void record_dive_to_table(struct dive *dive, struct dive_table *table)
|
||||
{
|
||||
add_to_dive_table(table, table->nr, fixup_dive(dive));
|
||||
}
|
||||
|
||||
extern "C" void start_match(const char *type, const char *name, char *buffer)
|
||||
{
|
||||
if (verbose > 2)
|
||||
printf("Matching %s '%s' (%s)\n",
|
||||
type, name, buffer);
|
||||
}
|
||||
|
||||
extern "C" void nonmatch(const char *type, const char *name, char *buffer)
|
||||
{
|
||||
if (verbose > 1)
|
||||
printf("Unable to match %s '%s' (%s)\n",
|
||||
type, name, buffer);
|
||||
}
|
||||
|
||||
extern "C" void event_start(struct parser_state *state)
|
||||
{
|
||||
memset(&state->cur_event, 0, sizeof(state->cur_event));
|
||||
state->cur_event.deleted = 0; /* Active */
|
||||
}
|
||||
|
||||
extern "C" void event_end(struct parser_state *state)
|
||||
{
|
||||
struct divecomputer *dc = get_dc(state);
|
||||
if (state->cur_event.type == 123) {
|
||||
struct picture pic = empty_picture;
|
||||
pic.filename = strdup(state->cur_event.name);
|
||||
/* theoretically this could fail - but we didn't support multi year offsets */
|
||||
pic.offset.seconds = state->cur_event.time.seconds;
|
||||
add_picture(&state->cur_dive->pictures, pic); /* Takes ownership. */
|
||||
} else {
|
||||
struct event *ev;
|
||||
/* 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.
|
||||
*/
|
||||
if (state->cur_event.type == 0 && strcmp(state->cur_event.name, "gaschange") == 0)
|
||||
state->cur_event.type = state->cur_event.value >> 16 > 0 ? SAMPLE_EVENT_GASCHANGE2 : SAMPLE_EVENT_GASCHANGE;
|
||||
ev = add_event(dc, state->cur_event.time.seconds,
|
||||
state->cur_event.type, state->cur_event.flags,
|
||||
state->cur_event.value, state->cur_event.name);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
if (ev && state->cur_event.time.seconds == 0 && state->cur_event.type == SAMPLE_EVENT_PO2 && state->cur_event.value && dc->divemode==OC) {
|
||||
dc->divemode = CCR;
|
||||
}
|
||||
|
||||
if (ev && event_is_gaschange(ev)) {
|
||||
/* See try_to_fill_event() on why the filled-in index is one too big */
|
||||
ev->gas.index = state->cur_event.gas.index-1;
|
||||
if (state->cur_event.gas.mix.o2.permille || state->cur_event.gas.mix.he.permille)
|
||||
ev->gas.mix = state->cur_event.gas.mix;
|
||||
}
|
||||
}
|
||||
state->cur_event.deleted = 1; /* No longer active */
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
extern "C" bool is_dive(struct parser_state *state)
|
||||
{
|
||||
return state->cur_dive &&
|
||||
(state->cur_dive->dive_site || state->cur_dive->when || state->cur_dive->dc.samples);
|
||||
}
|
||||
|
||||
extern "C" void reset_dc_info(struct divecomputer *, struct parser_state *state)
|
||||
{
|
||||
/* WARN: reset dc info does't touch the dc? */
|
||||
state->lastcylinderindex = 0;
|
||||
}
|
||||
|
||||
extern "C" void reset_dc_settings(struct parser_state *state)
|
||||
{
|
||||
free((void *)state->cur_settings.dc.model);
|
||||
free((void *)state->cur_settings.dc.nickname);
|
||||
free((void *)state->cur_settings.dc.serial_nr);
|
||||
free((void *)state->cur_settings.dc.firmware);
|
||||
state->cur_settings.dc.model = NULL;
|
||||
state->cur_settings.dc.nickname = NULL;
|
||||
state->cur_settings.dc.serial_nr = NULL;
|
||||
state->cur_settings.dc.firmware = NULL;
|
||||
state->cur_settings.dc.deviceid = 0;
|
||||
}
|
||||
|
||||
extern "C" void reset_fingerprint(struct parser_state *state)
|
||||
{
|
||||
free((void *)state->cur_settings.fingerprint.data);
|
||||
state->cur_settings.fingerprint.data = NULL;
|
||||
state->cur_settings.fingerprint.model = 0;
|
||||
state->cur_settings.fingerprint.serial = 0;
|
||||
state->cur_settings.fingerprint.fdeviceid = 0;
|
||||
state->cur_settings.fingerprint.fdiveid = 0;
|
||||
}
|
||||
|
||||
extern "C" void settings_start(struct parser_state *state)
|
||||
{
|
||||
state->in_settings = true;
|
||||
}
|
||||
|
||||
extern "C" void settings_end(struct parser_state *state)
|
||||
{
|
||||
state->in_settings = false;
|
||||
}
|
||||
|
||||
extern "C" void fingerprint_settings_start(struct parser_state *state)
|
||||
{
|
||||
reset_fingerprint(state);
|
||||
}
|
||||
|
||||
extern "C" void fingerprint_settings_end(struct parser_state *state)
|
||||
{
|
||||
create_fingerprint_node_from_hex(state->fingerprints,
|
||||
state->cur_settings.fingerprint.model,
|
||||
state->cur_settings.fingerprint.serial,
|
||||
state->cur_settings.fingerprint.data,
|
||||
state->cur_settings.fingerprint.fdeviceid,
|
||||
state->cur_settings.fingerprint.fdiveid);
|
||||
}
|
||||
|
||||
extern "C" void dc_settings_start(struct parser_state *state)
|
||||
{
|
||||
reset_dc_settings(state);
|
||||
}
|
||||
|
||||
extern "C" void dc_settings_end(struct parser_state *state)
|
||||
{
|
||||
create_device_node(state->log->devices,
|
||||
state->cur_settings.dc.model,
|
||||
state->cur_settings.dc.serial_nr,
|
||||
state->cur_settings.dc.nickname);
|
||||
reset_dc_settings(state);
|
||||
}
|
||||
|
||||
extern "C" void dive_site_start(struct parser_state *state)
|
||||
{
|
||||
if (state->cur_dive_site)
|
||||
return;
|
||||
state->taxonomy_category = -1;
|
||||
state->taxonomy_origin = -1;
|
||||
state->cur_dive_site = (dive_site *)calloc(1, sizeof(struct dive_site));
|
||||
}
|
||||
|
||||
extern "C" void dive_site_end(struct parser_state *state)
|
||||
{
|
||||
if (!state->cur_dive_site)
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
extern "C" void filter_preset_start(struct parser_state *state)
|
||||
{
|
||||
if (state->cur_filter)
|
||||
return;
|
||||
state->cur_filter = alloc_filter_preset();
|
||||
}
|
||||
|
||||
extern "C" void filter_preset_end(struct parser_state *state)
|
||||
{
|
||||
add_filter_preset_to_table(state->cur_filter, state->log->filter_presets);
|
||||
free_filter_preset(state->cur_filter);
|
||||
state->cur_filter = NULL;
|
||||
}
|
||||
|
||||
extern "C" void fulltext_start(struct parser_state *state)
|
||||
{
|
||||
if (!state->cur_filter)
|
||||
return;
|
||||
state->in_fulltext = true;
|
||||
}
|
||||
|
||||
extern "C" void fulltext_end(struct parser_state *state)
|
||||
{
|
||||
if (!state->in_fulltext)
|
||||
return;
|
||||
filter_preset_set_fulltext(state->cur_filter, state->fulltext, state->fulltext_string_mode);
|
||||
free(state->fulltext);
|
||||
free(state->fulltext_string_mode);
|
||||
state->fulltext = NULL;
|
||||
state->fulltext_string_mode = NULL;
|
||||
state->in_fulltext = false;
|
||||
}
|
||||
|
||||
extern "C" void filter_constraint_start(struct parser_state *state)
|
||||
{
|
||||
if (!state->cur_filter)
|
||||
return;
|
||||
state->in_filter_constraint = true;
|
||||
}
|
||||
|
||||
extern "C" void filter_constraint_end(struct parser_state *state)
|
||||
{
|
||||
if (!state->in_filter_constraint)
|
||||
return;
|
||||
filter_preset_add_constraint(state->cur_filter, state->filter_constraint_type, state->filter_constraint_string_mode,
|
||||
state->filter_constraint_range_mode, state->filter_constraint_negate, state->filter_constraint);
|
||||
free(state->filter_constraint_type);
|
||||
free(state->filter_constraint_string_mode);
|
||||
free(state->filter_constraint_range_mode);
|
||||
free(state->filter_constraint);
|
||||
|
||||
state->filter_constraint_type = NULL;
|
||||
state->filter_constraint_string_mode = NULL;
|
||||
state->filter_constraint_range_mode = NULL;
|
||||
state->filter_constraint_negate = false;
|
||||
state->filter_constraint = NULL;
|
||||
state->in_filter_constraint = false;
|
||||
}
|
||||
|
||||
extern "C" void dive_start(struct parser_state *state)
|
||||
{
|
||||
if (state->cur_dive)
|
||||
return;
|
||||
state->cur_dive = alloc_dive();
|
||||
reset_dc_info(&state->cur_dive->dc, state);
|
||||
memset(&state->cur_tm, 0, sizeof(state->cur_tm));
|
||||
state->o2pressure_sensor = 1;
|
||||
}
|
||||
|
||||
extern "C" void dive_end(struct parser_state *state)
|
||||
{
|
||||
if (!state->cur_dive)
|
||||
return;
|
||||
if (!is_dive(state)) {
|
||||
free_dive(state->cur_dive);
|
||||
} else {
|
||||
record_dive_to_table(state->cur_dive, state->log->dives);
|
||||
if (state->cur_trip)
|
||||
add_dive_to_trip(state->cur_dive, state->cur_trip);
|
||||
}
|
||||
state->cur_dive = NULL;
|
||||
state->cur_dc = NULL;
|
||||
state->cur_location.lat.udeg = 0;
|
||||
state->cur_location.lon.udeg = 0;
|
||||
}
|
||||
|
||||
extern "C" void trip_start(struct parser_state *state)
|
||||
{
|
||||
if (state->cur_trip)
|
||||
return;
|
||||
dive_end(state);
|
||||
state->cur_trip = alloc_trip();
|
||||
memset(&state->cur_tm, 0, sizeof(state->cur_tm));
|
||||
}
|
||||
|
||||
extern "C" void trip_end(struct parser_state *state)
|
||||
{
|
||||
if (!state->cur_trip)
|
||||
return;
|
||||
insert_trip(state->cur_trip, state->log->trips);
|
||||
state->cur_trip = NULL;
|
||||
}
|
||||
|
||||
extern "C" void picture_start(struct parser_state *state)
|
||||
{
|
||||
}
|
||||
|
||||
extern "C" void picture_end(struct parser_state *state)
|
||||
{
|
||||
add_picture(&state->cur_dive->pictures, state->cur_picture);
|
||||
/* dive_add_picture took ownership, we can just clear out copy of the data */
|
||||
state->cur_picture = empty_picture;
|
||||
}
|
||||
|
||||
extern "C" cylinder_t *cylinder_start(struct parser_state *state)
|
||||
{
|
||||
return add_empty_cylinder(&state->cur_dive->cylinders);
|
||||
}
|
||||
|
||||
extern "C" void cylinder_end(struct parser_state *state)
|
||||
{
|
||||
}
|
||||
|
||||
extern "C" void ws_start(struct parser_state *state)
|
||||
{
|
||||
add_cloned_weightsystem(&state->cur_dive->weightsystems, empty_weightsystem);
|
||||
}
|
||||
|
||||
extern "C" void ws_end(struct parser_state *state)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* If the given cylinder doesn't exist, return NO_SENSOR.
|
||||
*/
|
||||
static int sanitize_sensor_id(const struct dive *d, int nr)
|
||||
{
|
||||
return d && nr >= 0 && nr < d->cylinders.nr ? nr : NO_SENSOR;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
extern "C" void sample_start(struct parser_state *state)
|
||||
{
|
||||
struct divecomputer *dc = get_dc(state);
|
||||
struct sample *sample = prepare_sample(dc);
|
||||
|
||||
if (sample != dc->sample) {
|
||||
memcpy(sample, sample-1, sizeof(struct sample));
|
||||
sample->pressure[0].mbar = 0;
|
||||
sample->pressure[1].mbar = 0;
|
||||
} else {
|
||||
sample->sensor[0] = sanitize_sensor_id(state->cur_dive, !state->o2pressure_sensor);
|
||||
sample->sensor[1] = sanitize_sensor_id(state->cur_dive, state->o2pressure_sensor);
|
||||
}
|
||||
state->cur_sample = sample;
|
||||
state->next_o2_sensor = 0;
|
||||
}
|
||||
|
||||
extern "C" void sample_end(struct parser_state *state)
|
||||
{
|
||||
if (!state->cur_dive)
|
||||
return;
|
||||
|
||||
finish_sample(get_dc(state));
|
||||
state->cur_sample = NULL;
|
||||
}
|
||||
|
||||
extern "C" void divecomputer_start(struct parser_state *state)
|
||||
{
|
||||
struct divecomputer *dc;
|
||||
|
||||
/* Start from the previous dive computer */
|
||||
dc = &state->cur_dive->dc;
|
||||
while (dc->next)
|
||||
dc = dc->next;
|
||||
|
||||
/* Did we already fill that in? */
|
||||
if (dc->samples || dc->model || dc->when) {
|
||||
struct divecomputer *newdc = (divecomputer *)calloc(1, sizeof(*newdc));
|
||||
if (newdc) {
|
||||
dc->next = newdc;
|
||||
dc = newdc;
|
||||
}
|
||||
}
|
||||
|
||||
/* .. this is the one we'll use */
|
||||
state->cur_dc = dc;
|
||||
reset_dc_info(dc, state);
|
||||
}
|
||||
|
||||
extern "C" void divecomputer_end(struct parser_state *state)
|
||||
{
|
||||
if (!state->cur_dc->when)
|
||||
state->cur_dc->when = state->cur_dive->when;
|
||||
state->cur_dc = NULL;
|
||||
}
|
||||
|
||||
extern "C" void userid_start(struct parser_state *state)
|
||||
{
|
||||
state->in_userid = true;
|
||||
}
|
||||
|
||||
extern "C" void userid_stop(struct parser_state *state)
|
||||
{
|
||||
state->in_userid = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy whitespace-trimmed string. Warning: the passed in string will be freed,
|
||||
* therefore make sure to only pass in to NULL-initialized pointers or pointers
|
||||
* to owned strings
|
||||
*/
|
||||
extern "C" void utf8_string(const char *buffer, void *_res)
|
||||
{
|
||||
char **res = (char **)_res;
|
||||
free(*res);
|
||||
while (isspace(*buffer))
|
||||
++buffer;
|
||||
if (!*buffer) {
|
||||
*res = strdup("");
|
||||
return;
|
||||
}
|
||||
const char *end = buffer + strlen(buffer);
|
||||
while (isspace(end[-1]))
|
||||
--end;
|
||||
size_t len = end - buffer;
|
||||
*res = (char *)malloc(len + 1);
|
||||
memcpy(*res, buffer, len);
|
||||
(*res)[len] = '\0';
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
extern "C" void add_dive_site(const char *ds_name, struct dive *dive, struct parser_state *state)
|
||||
{
|
||||
const char *buffer = ds_name;
|
||||
std::string trimmed = trimspace(buffer);
|
||||
if (!trimmed.empty()) {
|
||||
struct dive_site *ds = dive->dive_site;
|
||||
if (!ds) {
|
||||
// if the dive doesn't have a dive site, check if there's already a dive site by this name
|
||||
ds = get_dive_site_by_name(trimmed.c_str(), state->log->sites);
|
||||
}
|
||||
if (ds) {
|
||||
// we have a dive site, let's hope there isn't a different name
|
||||
if (empty_string(ds->name)) {
|
||||
ds->name = copy_string(trimmed.c_str());
|
||||
} else if (trimmed != ds->name) {
|
||||
// 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
|
||||
struct dive_site *exact_match = get_dive_site_by_gps_and_name(trimmed.c_str(), &ds->location, state->log->sites);
|
||||
if (exact_match) {
|
||||
unregister_dive_from_dive_site(dive);
|
||||
add_dive_to_dive_site(dive, exact_match);
|
||||
} else {
|
||||
struct dive_site *newds = create_dive_site(trimmed.c_str(), state->log->sites);
|
||||
unregister_dive_from_dive_site(dive);
|
||||
add_dive_to_dive_site(dive, newds);
|
||||
if (has_location(&state->cur_location)) {
|
||||
// we started this uuid with GPS data, so lets use those
|
||||
newds->location = state->cur_location;
|
||||
} else {
|
||||
newds->location = ds->location;
|
||||
}
|
||||
newds->notes = add_to_string(newds->notes, translate("gettextFromC", "additional name for site: %s\n"), ds->name);
|
||||
}
|
||||
} else if (dive->dive_site != ds) {
|
||||
// add the existing dive site to the current dive
|
||||
unregister_dive_from_dive_site(dive);
|
||||
add_dive_to_dive_site(dive, ds);
|
||||
}
|
||||
} else {
|
||||
add_dive_to_dive_site(dive, create_dive_site(trimmed.c_str(), state->log->sites));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int atoi_n(char *ptr, unsigned int len)
|
||||
{
|
||||
if (len < 10) {
|
||||
char buf[10];
|
||||
|
||||
memcpy(buf, ptr, len);
|
||||
buf[len] = 0;
|
||||
return atoi(buf);
|
||||
}
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue