summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Miika Turkia <miika.turkia@gmail.com>2017-11-27 19:41:10 +0200
committerGravatar Dirk Hohndel <dirk@hohndel.org>2017-11-27 13:42:10 -0800
commit02c80a60b0cb7e2144ff8ef8564587299c9540b9 (patch)
tree8b8f9f1aaded96530b04325f953b3d730277d9c5
parent0c564a4579c96704cbc2f2aa290edc538a9d5c6a (diff)
downloadsubsurface-02c80a60b0cb7e2144ff8ef8564587299c9540b9.tar.gz
Refactore parse-xml.c into parse.c and parse-xml.c
This should help us to move parsing that is not XML related to other files, hopefully making the code cleaner. Signed-off-by: Miika Turkia <miika.turkia@gmail.com>
-rw-r--r--core/CMakeLists.txt1
-rw-r--r--core/membuffer.h1
-rw-r--r--core/parse-xml.c447
-rw-r--r--core/parse.c449
-rw-r--r--core/parse.h96
-rw-r--r--packaging/ios/Subsurface-mobile/Subsurface-mobile.pro1
-rw-r--r--tests/testparse.cpp1
7 files changed, 550 insertions, 446 deletions
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
index f063f3aa5..31b82ebbb 100644
--- a/core/CMakeLists.txt
+++ b/core/CMakeLists.txt
@@ -50,6 +50,7 @@ set(SUBSURFACE_CORE_LIB_SRCS
membuffer.c
ostctools.c
parse-xml.c
+ parse.c
planner.c
plannernotes.c
profile.c
diff --git a/core/membuffer.h b/core/membuffer.h
index fd59d7140..bd0a46d3a 100644
--- a/core/membuffer.h
+++ b/core/membuffer.h
@@ -7,6 +7,7 @@ extern "C" {
#endif
#include <ctype.h>
+#include "units.h"
struct membuffer {
unsigned int len, alloc;
diff --git a/core/parse-xml.c b/core/parse-xml.c
index 6fd298710..4de4ab867 100644
--- a/core/parse-xml.c
+++ b/core/parse-xml.c
@@ -22,6 +22,7 @@
#include "gettext.h"
#include "dive.h"
+#include "parse.h"
#include "divelist.h"
#include "device.h"
#include "membuffer.h"
@@ -33,148 +34,10 @@ int diveid = -1;
static xmlDoc *test_xslt_transforms(xmlDoc *doc, const char **params);
-/* the dive table holds the overall dive list; target table points at
- * the table we are currently filling */
-struct dive_table dive_table;
-struct dive_table *target_table = NULL;
-
-/* Trim a character string by removing leading and trailing white space characters.
- * Parameter: a pointer to a null-terminated character string (buffer);
- * Return value: length of the trimmed string, excluding the terminal 0x0 byte
- * The original pointer (buffer) remains valid after this function has been called
- * and points to the trimmed string */
-int trimspace(char *buffer)
-{
- int i, size, start, end;
- size = strlen(buffer);
-
- if (!size)
- return 0;
- for(start = 0; isspace(buffer[start]); start++)
- if (start >= size) return 0; // Find 1st character following leading whitespace
- for(end = size - 1; isspace(buffer[end]); end--) // Find last character before trailing whitespace
- if (end <= 0) return 0;
- for(i = start; i <= end; i++) // Move the nonspace characters to the start of the string
- buffer[i-start] = buffer[i];
- size = end - start + 1;
- buffer[size] = 0x0; // then terminate the string
- return size; // return string length
-}
-
-/*
- * Clear a dive_table
- */
-void clear_table(struct dive_table *table)
-{
- for (int i = 0; i < table->nr; i++)
- free(table->dives[i]);
- table->nr = 0;
-}
-
-/*
- * Add a dive into the dive_table array
- */
-void record_dive_to_table(struct dive *dive, struct dive_table *table)
-{
- assert(table != NULL);
- struct dive **dives = grow_dive_table(table);
- int nr = table->nr;
-
- dives[nr] = fixup_dive(dive);
- table->nr = nr + 1;
-}
-
-void record_dive(struct dive *dive)
-{
- record_dive_to_table(dive, &dive_table);
-}
-
-static void start_match(const char *type, const char *name, char *buffer)
-{
- if (verbose > 2)
- printf("Matching %s '%s' (%s)\n",
- type, name, buffer);
-}
-
-static void nonmatch(const char *type, const char *name, char *buffer)
-{
- if (verbose > 1)
- printf("Unable to match %s '%s' (%s)\n",
- type, name, buffer);
-}
-
-typedef void (*matchfn_t)(char *buffer, void *);
-
-static int match(const char *pattern, int plen,
- const char *name,
- matchfn_t fn, char *buf, void *data)
-{
- switch (name[plen]) {
- case '\0':
- case '.':
- break;
- default:
- return 0;
- }
- if (memcmp(pattern, name, plen))
- return 0;
- fn(buf, data);
- return 1;
-}
-
-
struct units xml_parsing_units;
const struct units SI_units = SI_UNITS;
const struct units IMPERIAL_units = IMPERIAL_UNITS;
-/*
- * Dive info as it is being built up..
- */
-#define MAX_EVENT_NAME 128
-static struct divecomputer *cur_dc;
-static struct dive *cur_dive;
-static struct dive_site *cur_dive_site;
-degrees_t cur_latitude, cur_longitude;
-static dive_trip_t *cur_trip = NULL;
-static struct sample *cur_sample;
-static struct picture *cur_picture;
-static union {
- struct event event;
- char allocation[sizeof(struct event)+MAX_EVENT_NAME];
-} event_allocation = { .event.deleted = 1 };
-#define cur_event event_allocation.event
-static struct {
- struct {
- const char *model;
- uint32_t deviceid;
- const char *nickname, *serial_nr, *firmware;
- } dc;
-} cur_settings;
-static bool in_settings = false;
-static bool in_userid = false;
-static struct tm cur_tm;
-static int cur_cylinder_index, cur_ws_index;
-static int lastcylinderindex, next_o2_sensor;
-static int o2pressure_sensor;
-static struct extra_data cur_extra_data;
-
-/*
- * If we don't have an explicit dive computer,
- * we use the implicit one that every dive has..
- */
-static struct divecomputer *get_dc(void)
-{
- return cur_dc ?: &cur_dive->dc;
-}
-
-static enum import_source {
- UNKNOWN,
- LIBDIVECOMPUTER,
- DIVINGLOG,
- UDDF,
- SSRF_WS,
-} import_source;
-
static void divedate(const char *buffer, timestamp_t *when)
{
int d, m, y;
@@ -561,15 +424,6 @@ static void cylindersize(char *buffer, volume_t *volume)
}
}
-static void utf8_string(char *buffer, void *_res)
-{
- char **res = _res;
- int size;
- size = trimspace(buffer);
- if(size)
- *res = strdup(buffer);
-}
-
static void event_name(char *buffer, char *name)
{
int size = trimspace(buffer);
@@ -1537,305 +1391,6 @@ static void try_to_fill_dive_site(struct dive_site **ds_p, const char *name, cha
nonmatch("divesite", name, buf);
}
-/*
- * 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.
- */
-static bool is_dive(void)
-{
- return (cur_dive &&
- (cur_dive->dive_site_uuid || cur_dive->when || cur_dive->dc.samples));
-}
-
-static void reset_dc_info(struct divecomputer *dc)
-{
- /* WARN: reset dc info does't touch the dc? */
- (void) dc;
- lastcylinderindex = 0;
-}
-
-static void reset_dc_settings(void)
-{
- free((void *)cur_settings.dc.model);
- free((void *)cur_settings.dc.nickname);
- free((void *)cur_settings.dc.serial_nr);
- free((void *)cur_settings.dc.firmware);
- cur_settings.dc.model = NULL;
- cur_settings.dc.nickname = NULL;
- cur_settings.dc.serial_nr = NULL;
- cur_settings.dc.firmware = NULL;
- cur_settings.dc.deviceid = 0;
-}
-
-static void settings_start(void)
-{
- in_settings = true;
-}
-
-static void settings_end(void)
-{
- in_settings = false;
-}
-
-static void dc_settings_start(void)
-{
- reset_dc_settings();
-}
-
-static void dc_settings_end(void)
-{
- create_device_node(cur_settings.dc.model, cur_settings.dc.deviceid, cur_settings.dc.serial_nr,
- cur_settings.dc.firmware, cur_settings.dc.nickname);
- reset_dc_settings();
-}
-
-static void dive_site_start(void)
-{
- if (cur_dive_site)
- return;
- cur_dive_site = calloc(1, sizeof(struct dive_site));
-}
-
-static void dive_site_end(void)
-{
- if (!cur_dive_site)
- return;
- if (cur_dive_site->taxonomy.nr == 0) {
- free(cur_dive_site->taxonomy.category);
- cur_dive_site->taxonomy.category = NULL;
- }
- if (cur_dive_site->uuid) {
- struct dive_site *ds = alloc_or_get_dive_site(cur_dive_site->uuid);
- merge_dive_site(ds, cur_dive_site);
-
- if (verbose > 3)
- printf("completed dive site uuid %x8 name {%s}\n", ds->uuid, ds->name);
- }
- free_taxonomy(&cur_dive_site->taxonomy);
- free(cur_dive_site);
- cur_dive_site = NULL;
-}
-
-// now we need to add the code to parse the parts of the divesite enry
-
-static void dive_start(void)
-{
- if (cur_dive)
- return;
- cur_dive = alloc_dive();
- reset_dc_info(&cur_dive->dc);
- memset(&cur_tm, 0, sizeof(cur_tm));
- if (cur_trip) {
- add_dive_to_trip(cur_dive, cur_trip);
- cur_dive->tripflag = IN_TRIP;
- }
- o2pressure_sensor = 1;
-}
-
-static void dive_end(void)
-{
- if (!cur_dive)
- return;
- if (!is_dive())
- free(cur_dive);
- else
- record_dive_to_table(cur_dive, target_table);
- cur_dive = NULL;
- cur_dc = NULL;
- cur_latitude.udeg = 0;
- cur_longitude.udeg = 0;
- cur_cylinder_index = 0;
- cur_ws_index = 0;
-}
-
-static void trip_start(void)
-{
- if (cur_trip)
- return;
- dive_end();
- cur_trip = calloc(1, sizeof(dive_trip_t));
- memset(&cur_tm, 0, sizeof(cur_tm));
-}
-
-static void trip_end(void)
-{
- if (!cur_trip)
- return;
- insert_trip(&cur_trip);
- cur_trip = NULL;
-}
-
-static void event_start(void)
-{
- memset(&cur_event, 0, sizeof(cur_event));
- cur_event.deleted = 0; /* Active */
-}
-
-static void event_end(void)
-{
- struct divecomputer *dc = get_dc();
- if (cur_event.type == 123) {
- struct picture *pic = alloc_picture();
- pic->filename = strdup(cur_event.name);
- /* theoretically this could fail - but we didn't support multi year offsets */
- pic->offset.seconds = cur_event.time.seconds;
- dive_add_picture(cur_dive, pic);
- } 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 (cur_event.type == 0 && strcmp(cur_event.name, "gaschange") == 0)
- cur_event.type = cur_event.value >> 16 > 0 ? SAMPLE_EVENT_GASCHANGE2 : SAMPLE_EVENT_GASCHANGE;
- ev = add_event(dc, cur_event.time.seconds,
- cur_event.type, cur_event.flags,
- cur_event.value, 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 && cur_event.time.seconds == 0 && cur_event.type == SAMPLE_EVENT_PO2 && 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 = cur_event.gas.index-1;
- if (cur_event.gas.mix.o2.permille || cur_event.gas.mix.he.permille)
- ev->gas.mix = cur_event.gas.mix;
- }
- }
- cur_event.deleted = 1; /* No longer active */
-}
-
-static void picture_start(void)
-{
- cur_picture = alloc_picture();
-}
-
-static void picture_end(void)
-{
- dive_add_picture(cur_dive, cur_picture);
- cur_picture = NULL;
-}
-
-static void cylinder_start(void)
-{
-}
-
-static void cylinder_end(void)
-{
- cur_cylinder_index++;
-}
-
-static void ws_start(void)
-{
-}
-
-static void ws_end(void)
-{
- cur_ws_index++;
-}
-
-/*
- * 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.
- */
-static void sample_start(void)
-{
- struct divecomputer *dc = get_dc();
- 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] = !o2pressure_sensor;
- sample->sensor[1] = o2pressure_sensor;
- }
- cur_sample = sample;
- next_o2_sensor = 0;
-}
-
-static void sample_end(void)
-{
- if (!cur_dive)
- return;
-
- finish_sample(get_dc());
- cur_sample = NULL;
-}
-
-static void divecomputer_start(void)
-{
- struct divecomputer *dc;
-
- /* Start from the previous dive computer */
- dc = &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 = calloc(1, sizeof(*newdc));
- if (newdc) {
- dc->next = newdc;
- dc = newdc;
- }
- }
-
- /* .. this is the one we'll use */
- cur_dc = dc;
- reset_dc_info(dc);
-}
-
-static void divecomputer_end(void)
-{
- if (!cur_dc->when)
- cur_dc->when = cur_dive->when;
- cur_dc = NULL;
-}
-
-static void userid_start(void)
-{
- in_userid = true;
- //if the xml contains userid, keep saving it.
- // don't call the prefs method here as we don't wanna
- // actually change the preferences, this is temporary and
- // will be reverted when the file finishes.
-
- prefs.save_userid_local = true;
-}
-
-static void userid_stop(void)
-{
- in_userid = false;
-}
-
static bool entry(const char *name, char *buf)
{
if (!strncmp(name, "version.program", sizeof("version.program") - 1) ||
diff --git a/core/parse.c b/core/parse.c
new file mode 100644
index 000000000..ba440f1b9
--- /dev/null
+++ b/core/parse.c
@@ -0,0 +1,449 @@
+#include <stdio.h>
+#include <assert.h>
+#include "membuffer.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <libdivecomputer/parser.h>
+
+#include "dive.h"
+#include "parse.h"
+#include "divelist.h"
+#include "device.h"
+
+/*
+static union {
+ struct event event;
+ char allocation[sizeof(struct event)+MAX_EVENT_NAME];
+} event_allocation = { .event.deleted = 1 };
+
+static event_allocation cur_event = { .event.deleted = 1 };
+static inline union {
+ struct event event;
+ char allocation[sizeof(struct event)+MAX_EVENT_NAME];
+} event_allocation = { .event.deleted = 1 };
+#define cur_event event_allocation.event
+*/
+event_allocation_t event_allocation = { .event.deleted = 1 };
+
+
+struct divecomputer *cur_dc = NULL;
+struct dive *cur_dive = NULL;
+struct dive_site *cur_dive_site = NULL;
+degrees_t cur_latitude, cur_longitude;
+dive_trip_t *cur_trip = NULL;
+struct sample *cur_sample = NULL;
+struct picture *cur_picture = NULL;
+
+bool in_settings = false;
+bool in_userid = false;
+struct tm cur_tm;
+int cur_cylinder_index, cur_ws_index;
+int lastcylinderindex, next_o2_sensor;
+int o2pressure_sensor;
+struct extra_data cur_extra_data;
+
+struct dive_table dive_table;
+struct dive_table *target_table = NULL;
+
+/*
+ * If we don't have an explicit dive computer,
+ * we use the implicit one that every dive has..
+ */
+struct divecomputer *get_dc(void)
+{
+ return cur_dc ?: &cur_dive->dc;
+}
+
+
+/* Trim a character string by removing leading and trailing white space characters.
+ * Parameter: a pointer to a null-terminated character string (buffer);
+ * Return value: length of the trimmed string, excluding the terminal 0x0 byte
+ * The original pointer (buffer) remains valid after this function has been called
+ * and points to the trimmed string */
+int trimspace(char *buffer)
+{
+ int i, size, start, end;
+ size = strlen(buffer);
+
+ if (!size)
+ return 0;
+ for(start = 0; isspace(buffer[start]); start++)
+ if (start >= size) return 0; // Find 1st character following leading whitespace
+ for(end = size - 1; isspace(buffer[end]); end--) // Find last character before trailing whitespace
+ if (end <= 0) return 0;
+ for(i = start; i <= end; i++) // Move the nonspace characters to the start of the string
+ buffer[i-start] = buffer[i];
+ size = end - start + 1;
+ buffer[size] = 0x0; // then terminate the string
+ return size; // return string length
+}
+
+/*
+ * Clear a dive_table
+ */
+void clear_table(struct dive_table *table)
+{
+ for (int i = 0; i < table->nr; i++)
+ free(table->dives[i]);
+ table->nr = 0;
+}
+
+/*
+ * Add a dive into the dive_table array
+ */
+void record_dive_to_table(struct dive *dive, struct dive_table *table)
+{
+ assert(table != NULL);
+ struct dive **dives = grow_dive_table(table);
+ int nr = table->nr;
+
+ dives[nr] = fixup_dive(dive);
+ table->nr = nr + 1;
+}
+
+void record_dive(struct dive *dive)
+{
+ record_dive_to_table(dive, &dive_table);
+}
+
+void start_match(const char *type, const char *name, char *buffer)
+{
+ if (verbose > 2)
+ printf("Matching %s '%s' (%s)\n",
+ type, name, buffer);
+}
+
+void nonmatch(const char *type, const char *name, char *buffer)
+{
+ if (verbose > 1)
+ printf("Unable to match %s '%s' (%s)\n",
+ type, name, buffer);
+}
+
+typedef void (*matchfn_t)(char *buffer, void *);
+
+int match(const char *pattern, int plen,
+ const char *name,
+ matchfn_t fn, char *buf, void *data)
+{
+ switch (name[plen]) {
+ case '\0':
+ case '.':
+ break;
+ default:
+ return 0;
+ }
+ if (memcmp(pattern, name, plen))
+ return 0;
+ fn(buf, data);
+ return 1;
+}
+
+void event_start(void)
+{
+ memset(&cur_event, 0, sizeof(cur_event));
+ cur_event.deleted = 0; /* Active */
+}
+
+void event_end(void)
+{
+ struct divecomputer *dc = get_dc();
+ if (cur_event.type == 123) {
+ struct picture *pic = alloc_picture();
+ pic->filename = strdup(cur_event.name);
+ /* theoretically this could fail - but we didn't support multi year offsets */
+ pic->offset.seconds = cur_event.time.seconds;
+ dive_add_picture(cur_dive, pic);
+ } 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 (cur_event.type == 0 && strcmp(cur_event.name, "gaschange") == 0)
+ cur_event.type = cur_event.value >> 16 > 0 ? SAMPLE_EVENT_GASCHANGE2 : SAMPLE_EVENT_GASCHANGE;
+ ev = add_event(dc, cur_event.time.seconds,
+ cur_event.type, cur_event.flags,
+ cur_event.value, 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 && cur_event.time.seconds == 0 && cur_event.type == SAMPLE_EVENT_PO2 && 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 = cur_event.gas.index-1;
+ if (cur_event.gas.mix.o2.permille || cur_event.gas.mix.he.permille)
+ ev->gas.mix = cur_event.gas.mix;
+ }
+ }
+ 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.
+ */
+bool is_dive(void)
+{
+ return (cur_dive &&
+ (cur_dive->dive_site_uuid || cur_dive->when || cur_dive->dc.samples));
+}
+
+void reset_dc_info(struct divecomputer *dc)
+{
+ /* WARN: reset dc info does't touch the dc? */
+ (void) dc;
+ lastcylinderindex = 0;
+}
+
+void reset_dc_settings(void)
+{
+ free((void *)cur_settings.dc.model);
+ free((void *)cur_settings.dc.nickname);
+ free((void *)cur_settings.dc.serial_nr);
+ free((void *)cur_settings.dc.firmware);
+ cur_settings.dc.model = NULL;
+ cur_settings.dc.nickname = NULL;
+ cur_settings.dc.serial_nr = NULL;
+ cur_settings.dc.firmware = NULL;
+ cur_settings.dc.deviceid = 0;
+}
+
+void settings_start(void)
+{
+ in_settings = true;
+}
+
+void settings_end(void)
+{
+ in_settings = false;
+}
+
+void dc_settings_start(void)
+{
+ reset_dc_settings();
+}
+
+void dc_settings_end(void)
+{
+ create_device_node(cur_settings.dc.model, cur_settings.dc.deviceid, cur_settings.dc.serial_nr,
+ cur_settings.dc.firmware, cur_settings.dc.nickname);
+ reset_dc_settings();
+}
+
+void dive_site_start(void)
+{
+ if (cur_dive_site)
+ return;
+ cur_dive_site = calloc(1, sizeof(struct dive_site));
+}
+
+void dive_site_end(void)
+{
+ if (!cur_dive_site)
+ return;
+ if (cur_dive_site->taxonomy.nr == 0) {
+ free(cur_dive_site->taxonomy.category);
+ cur_dive_site->taxonomy.category = NULL;
+ }
+ if (cur_dive_site->uuid) {
+ struct dive_site *ds = alloc_or_get_dive_site(cur_dive_site->uuid);
+ merge_dive_site(ds, cur_dive_site);
+
+ if (verbose > 3)
+ printf("completed dive site uuid %x8 name {%s}\n", ds->uuid, ds->name);
+ }
+ free_taxonomy(&cur_dive_site->taxonomy);
+ free(cur_dive_site);
+ cur_dive_site = NULL;
+}
+
+// now we need to add the code to parse the parts of the divesite enry
+
+void dive_start(void)
+{
+ if (cur_dive)
+ return;
+ cur_dive = alloc_dive();
+ reset_dc_info(&cur_dive->dc);
+ memset(&cur_tm, 0, sizeof(cur_tm));
+ if (cur_trip) {
+ add_dive_to_trip(cur_dive, cur_trip);
+ cur_dive->tripflag = IN_TRIP;
+ }
+ o2pressure_sensor = 1;
+}
+
+void dive_end(void)
+{
+ if (!cur_dive)
+ return;
+ if (!is_dive())
+ free(cur_dive);
+ else
+ record_dive_to_table(cur_dive, target_table);
+ cur_dive = NULL;
+ cur_dc = NULL;
+ cur_latitude.udeg = 0;
+ cur_longitude.udeg = 0;
+ cur_cylinder_index = 0;
+ cur_ws_index = 0;
+}
+
+void trip_start(void)
+{
+ if (cur_trip)
+ return;
+ dive_end();
+ cur_trip = calloc(1, sizeof(dive_trip_t));
+ memset(&cur_tm, 0, sizeof(cur_tm));
+}
+
+void trip_end(void)
+{
+ if (!cur_trip)
+ return;
+ insert_trip(&cur_trip);
+ cur_trip = NULL;
+}
+
+void picture_start(void)
+{
+ cur_picture = alloc_picture();
+}
+
+void picture_end(void)
+{
+ dive_add_picture(cur_dive, cur_picture);
+ cur_picture = NULL;
+}
+
+void cylinder_start(void)
+{
+}
+
+void cylinder_end(void)
+{
+ cur_cylinder_index++;
+}
+
+void ws_start(void)
+{
+}
+
+void ws_end(void)
+{
+ cur_ws_index++;
+}
+
+/*
+ * 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.
+ */
+void sample_start(void)
+{
+ struct divecomputer *dc = get_dc();
+ 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] = !o2pressure_sensor;
+ sample->sensor[1] = o2pressure_sensor;
+ }
+ cur_sample = sample;
+ next_o2_sensor = 0;
+}
+
+void sample_end(void)
+{
+ if (!cur_dive)
+ return;
+
+ finish_sample(get_dc());
+ cur_sample = NULL;
+}
+
+void divecomputer_start(void)
+{
+ struct divecomputer *dc;
+
+ /* Start from the previous dive computer */
+ dc = &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 = calloc(1, sizeof(*newdc));
+ if (newdc) {
+ dc->next = newdc;
+ dc = newdc;
+ }
+ }
+
+ /* .. this is the one we'll use */
+ cur_dc = dc;
+ reset_dc_info(dc);
+}
+
+void divecomputer_end(void)
+{
+ if (!cur_dc->when)
+ cur_dc->when = cur_dive->when;
+ cur_dc = NULL;
+}
+
+void userid_start(void)
+{
+ in_userid = true;
+ //if the xml contains userid, keep saving it.
+ // don't call the prefs method here as we don't wanna
+ // actually change the preferences, this is temporary and
+ // will be reverted when the file finishes.
+
+ prefs.save_userid_local = true;
+}
+
+void userid_stop(void)
+{
+ in_userid = false;
+}
+
+void utf8_string(char *buffer, void *_res)
+{
+ char **res = _res;
+ int size;
+ size = trimspace(buffer);
+ if(size)
+ *res = strdup(buffer);
+}
+
diff --git a/core/parse.h b/core/parse.h
new file mode 100644
index 000000000..003ef9929
--- /dev/null
+++ b/core/parse.h
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0
+#ifndef PARSE_H
+#define PARSE_H
+
+#define MAX_EVENT_NAME 128
+
+typedef union {
+ struct event event;
+ char allocation[sizeof(struct event) + MAX_EVENT_NAME];
+} event_allocation_t;
+
+extern event_allocation_t event_allocation;
+#define cur_event event_allocation.event
+
+/*
+ * Dive info as it is being built up..
+ */
+extern struct divecomputer *cur_dc;
+extern struct dive *cur_dive;
+extern struct dive_site *cur_dive_site;
+extern degrees_t cur_latitude, cur_longitude;
+extern dive_trip_t *cur_trip;
+extern struct sample *cur_sample;
+extern struct picture *cur_picture;
+
+
+struct {
+ struct {
+ const char *model;
+ uint32_t deviceid;
+ const char *nickname, *serial_nr, *firmware;
+ } dc;
+} cur_settings;
+
+extern bool in_settings;
+extern bool in_userid;
+extern struct tm cur_tm;
+extern int cur_cylinder_index, cur_ws_index;
+extern int lastcylinderindex, next_o2_sensor;
+extern int o2pressure_sensor;
+extern struct extra_data cur_extra_data;
+
+enum import_source {
+ UNKNOWN,
+ LIBDIVECOMPUTER,
+ DIVINGLOG,
+ UDDF,
+ SSRF_WS,
+} import_source;
+
+/* the dive table holds the overall dive list; target table points at
+ * the table we are currently filling */
+extern struct dive_table dive_table;
+extern struct dive_table *target_table;
+
+int trimspace(char *buffer);
+void clear_table(struct dive_table *table);
+void record_dive_to_table(struct dive *dive, struct dive_table *table);
+void record_dive(struct dive *dive);
+void start_match(const char *type, const char *name, char *buffer);
+void nonmatch(const char *type, const char *name, char *buffer);
+typedef void (*matchfn_t)(char *buffer, void *);
+int match(const char *pattern, int plen, const char *name, matchfn_t fn, char *buf, void *data);
+void event_start(void);
+void event_end(void);
+struct divecomputer *get_dc(void);
+
+bool is_dive(void);
+void reset_dc_info(struct divecomputer *dc);
+void reset_dc_settings(void);
+void settings_start(void);
+void settings_end(void);
+void dc_settings_start(void);
+void dc_settings_end(void);
+void dive_site_start(void);
+void dive_site_end(void);
+void dive_start(void);
+void dive_end(void);
+void trip_start(void);
+void trip_end(void);
+void picture_start(void);
+void picture_end(void);
+void cylinder_start(void);
+void cylinder_end(void);
+void ws_start(void);
+void ws_end(void);
+
+void sample_start(void);
+void sample_end(void);
+void divecomputer_start(void);
+void divecomputer_end(void);
+void userid_start(void);
+void userid_stop(void);
+void utf8_string(char *buffer, void *_res);
+
+#endif
diff --git a/packaging/ios/Subsurface-mobile/Subsurface-mobile.pro b/packaging/ios/Subsurface-mobile/Subsurface-mobile.pro
index 8332f3759..836057c67 100644
--- a/packaging/ios/Subsurface-mobile/Subsurface-mobile.pro
+++ b/packaging/ios/Subsurface-mobile/Subsurface-mobile.pro
@@ -46,6 +46,7 @@ SOURCES += ../../../subsurface-mobile-main.cpp \
../../../core/liquivision.c \
../../../core/load-git.c \
../../../core/parse-xml.c \
+ ../../../core/parse.c \
../../../core/save-html.c \
../../../core/statistics.c \
../../../core/worldmap-save.c \
diff --git a/tests/testparse.cpp b/tests/testparse.cpp
index b614560b5..49d98f66e 100644
--- a/tests/testparse.cpp
+++ b/tests/testparse.cpp
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include "testparse.h"
#include "core/dive.h"
+#include "core/parse.h"
#include "core/file.h"
#include "core/divelist.h"
#include <QTextStream>