mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-30 22:20:21 +00:00
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>
This commit is contained in:
parent
0c564a4579
commit
02c80a60b0
7 changed files with 550 additions and 446 deletions
|
@ -50,6 +50,7 @@ set(SUBSURFACE_CORE_LIB_SRCS
|
|||
membuffer.c
|
||||
ostctools.c
|
||||
parse-xml.c
|
||||
parse.c
|
||||
planner.c
|
||||
plannernotes.c
|
||||
profile.c
|
||||
|
|
|
@ -7,6 +7,7 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
#include "units.h"
|
||||
|
||||
struct membuffer {
|
||||
unsigned int len, alloc;
|
||||
|
|
447
core/parse-xml.c
447
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) ||
|
||||
|
|
449
core/parse.c
Normal file
449
core/parse.c
Normal file
|
@ -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);
|
||||
}
|
||||
|
96
core/parse.h
Normal file
96
core/parse.h
Normal file
|
@ -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
|
|
@ -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 \
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue