From 195702046c9f2a1d6a1445c2735a634227838043 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 6 Feb 2016 15:27:40 +0100 Subject: [PATCH 1/5] Add a new abstract output interface. The new output interface provides the necessary infrastructure to add support for multiple output formats. Due to the abstract interface, each new format will require only minimal changes in the application itself. --- examples/Makefile.am | 3 ++ examples/output-private.h | 58 ++++++++++++++++++++++++++++ examples/output.c | 79 +++++++++++++++++++++++++++++++++++++++ examples/output.h | 43 +++++++++++++++++++++ 4 files changed, 183 insertions(+) create mode 100644 examples/output-private.h create mode 100644 examples/output.c create mode 100644 examples/output.h diff --git a/examples/Makefile.am b/examples/Makefile.am index cb13b64..a05af08 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -17,5 +17,8 @@ dctool_SOURCES = \ dctool_read.c \ dctool_write.c \ dctool_fwupdate.c \ + output.h \ + output-private.h \ + output.c \ utils.h \ utils.c diff --git a/examples/output-private.h b/examples/output-private.h new file mode 100644 index 0000000..db8cb06 --- /dev/null +++ b/examples/output-private.h @@ -0,0 +1,58 @@ +/* + * libdivecomputer + * + * Copyright (C) 2016 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef DCTOOL_OUTPUT_PRIVATE_H +#define DCTOOL_OUTPUT_PRIVATE_H + +#include +#include + +#include "output.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct dctool_output_vtable_t dctool_output_vtable_t; + +struct dctool_output_t { + const dctool_output_vtable_t *vtable; + unsigned int number; +}; + +struct dctool_output_vtable_t { + size_t size; + + dc_status_t (*write) (dctool_output_t *output, dc_parser_t *parser, const unsigned char data[], unsigned int size, const unsigned char fingerprint[], unsigned int fsize); + + dc_status_t (*free) (dctool_output_t *output); +}; + +dctool_output_t * +dctool_output_allocate (const dctool_output_vtable_t *vtable); + +void +dctool_output_deallocate (dctool_output_t *output); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DCTOOL_OUTPUT_PRIVATE_H */ diff --git a/examples/output.c b/examples/output.c new file mode 100644 index 0000000..510c929 --- /dev/null +++ b/examples/output.c @@ -0,0 +1,79 @@ +/* + * libdivecomputer + * + * Copyright (C) 2016 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include +#include + +#include "output-private.h" + +dctool_output_t * +dctool_output_allocate (const dctool_output_vtable_t *vtable) +{ + dctool_output_t *output = NULL; + + assert(vtable != NULL); + assert(vtable->size >= sizeof(dctool_output_t)); + + // Allocate memory. + output = (dctool_output_t *) malloc (vtable->size); + if (output == NULL) { + return output; + } + + output->vtable = vtable; + output->number = 0; + + return output; +} + +void +dctool_output_deallocate (dctool_output_t *output) +{ + free (output); +} + +dc_status_t +dctool_output_write (dctool_output_t *output, dc_parser_t *parser, const unsigned char data[], unsigned int size, const unsigned char fingerprint[], unsigned int fsize) +{ + if (output == NULL || output->vtable->write == NULL) + return DC_STATUS_SUCCESS; + + output->number++; + + return output->vtable->write (output, parser, data, size, fingerprint, fsize); +} + +dc_status_t +dctool_output_free (dctool_output_t *output) +{ + dc_status_t status = DC_STATUS_SUCCESS; + + if (output == NULL) + return DC_STATUS_SUCCESS; + + if (output->vtable->free) { + status = output->vtable->free (output); + } + + dctool_output_deallocate (output); + + return status; +} diff --git a/examples/output.h b/examples/output.h new file mode 100644 index 0000000..b11675f --- /dev/null +++ b/examples/output.h @@ -0,0 +1,43 @@ +/* + * libdivecomputer + * + * Copyright (C) 2016 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef DCTOOL_OUTPUT_H +#define DCTOOL_OUTPUT_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct dctool_output_t dctool_output_t; + +dc_status_t +dctool_output_write (dctool_output_t *output, dc_parser_t *parser, const unsigned char data[], unsigned int size, const unsigned char fingerprint[], unsigned int fsize); + +dc_status_t +dctool_output_free (dctool_output_t *output); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DCTOOL_OUTPUT_H */ From e35f0a3ff45dde9114bba1c896b64c226c065a78 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 6 Feb 2016 15:35:08 +0100 Subject: [PATCH 2/5] Add support for the xml output format. The XML output format exports all dives to a single xml file. --- examples/Makefile.am | 1 + examples/output.h | 3 + examples/output_xml.c | 365 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 369 insertions(+) create mode 100644 examples/output_xml.c diff --git a/examples/Makefile.am b/examples/Makefile.am index a05af08..3ae96d5 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -20,5 +20,6 @@ dctool_SOURCES = \ output.h \ output-private.h \ output.c \ + output_xml.c \ utils.h \ utils.c diff --git a/examples/output.h b/examples/output.h index b11675f..b123689 100644 --- a/examples/output.h +++ b/examples/output.h @@ -31,6 +31,9 @@ extern "C" { typedef struct dctool_output_t dctool_output_t; +dctool_output_t * +dctool_xml_output_new (const char *filename); + dc_status_t dctool_output_write (dctool_output_t *output, dc_parser_t *parser, const unsigned char data[], unsigned int size, const unsigned char fingerprint[], unsigned int fsize); diff --git a/examples/output_xml.c b/examples/output_xml.c new file mode 100644 index 0000000..a36e62a --- /dev/null +++ b/examples/output_xml.c @@ -0,0 +1,365 @@ +/* + * libdivecomputer + * + * Copyright (C) 2016 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include +#include +#include + +#include "output-private.h" +#include "utils.h" + +static dc_status_t dctool_xml_output_write (dctool_output_t *output, dc_parser_t *parser, const unsigned char data[], unsigned int size, const unsigned char fingerprint[], unsigned int fsize); +static dc_status_t dctool_xml_output_free (dctool_output_t *output); + +typedef struct dctool_xml_output_t { + dctool_output_t base; + FILE *ostream; +} dctool_xml_output_t; + +static const dctool_output_vtable_t xml_vtable = { + sizeof(dctool_xml_output_t), /* size */ + dctool_xml_output_write, /* write */ + dctool_xml_output_free, /* free */ +}; + +typedef struct sample_data_t { + FILE *ostream; + unsigned int nsamples; +} sample_data_t; + +static void +sample_cb (dc_sample_type_t type, dc_sample_value_t value, void *userdata) +{ + static const char *events[] = { + "none", "deco", "rbt", "ascent", "ceiling", "workload", "transmitter", + "violation", "bookmark", "surface", "safety stop", "gaschange", + "safety stop (voluntary)", "safety stop (mandatory)", "deepstop", + "ceiling (safety stop)", "floor", "divetime", "maxdepth", + "OLF", "PO2", "airtime", "rgbm", "heading", "tissue level warning", + "gaschange2"}; + static const char *decostop[] = { + "ndl", "safety", "deco", "deep"}; + + sample_data_t *sampledata = (sample_data_t *) userdata; + + switch (type) { + case DC_SAMPLE_TIME: + if (sampledata->nsamples++) + fprintf (sampledata->ostream, "\n"); + fprintf (sampledata->ostream, "\n"); + fprintf (sampledata->ostream, " \n", value.time / 60, value.time % 60); + break; + case DC_SAMPLE_DEPTH: + fprintf (sampledata->ostream, " %.2f\n", value.depth); + break; + case DC_SAMPLE_PRESSURE: + fprintf (sampledata->ostream, " %.2f\n", value.pressure.tank, value.pressure.value); + break; + case DC_SAMPLE_TEMPERATURE: + fprintf (sampledata->ostream, " %.2f\n", value.temperature); + break; + case DC_SAMPLE_EVENT: + if (value.event.type != SAMPLE_EVENT_GASCHANGE && value.event.type != SAMPLE_EVENT_GASCHANGE2) { + fprintf (sampledata->ostream, " %s\n", + value.event.type, value.event.time, value.event.flags, value.event.value, events[value.event.type]); + } + break; + case DC_SAMPLE_RBT: + fprintf (sampledata->ostream, " %u\n", value.rbt); + break; + case DC_SAMPLE_HEARTBEAT: + fprintf (sampledata->ostream, " %u\n", value.heartbeat); + break; + case DC_SAMPLE_BEARING: + fprintf (sampledata->ostream, " %u\n", value.bearing); + break; + case DC_SAMPLE_VENDOR: + fprintf (sampledata->ostream, " ", value.vendor.type, value.vendor.size); + for (unsigned int i = 0; i < value.vendor.size; ++i) + fprintf (sampledata->ostream, "%02X", ((unsigned char *) value.vendor.data)[i]); + fprintf (sampledata->ostream, "\n"); + break; + case DC_SAMPLE_SETPOINT: + fprintf (sampledata->ostream, " %.2f\n", value.setpoint); + break; + case DC_SAMPLE_PPO2: + fprintf (sampledata->ostream, " %.2f\n", value.ppo2); + break; + case DC_SAMPLE_CNS: + fprintf (sampledata->ostream, " %.1f\n", value.cns * 100.0); + break; + case DC_SAMPLE_DECO: + fprintf (sampledata->ostream, " %s\n", + value.deco.time, value.deco.depth, decostop[value.deco.type]); + break; + case DC_SAMPLE_GASMIX: + fprintf (sampledata->ostream, " %u\n", value.gasmix); + break; + default: + break; + } +} + +dctool_output_t * +dctool_xml_output_new (const char *filename) +{ + dctool_xml_output_t *output = NULL; + + if (filename == NULL) + goto error_exit; + + // Allocate memory. + output = (dctool_xml_output_t *) dctool_output_allocate (&xml_vtable); + if (output == NULL) { + goto error_exit; + } + + // Open the output file. + output->ostream = fopen (filename, "w"); + if (output->ostream == NULL) { + goto error_free; + } + + fprintf (output->ostream, "\n"); + + return (dctool_output_t *) output; + +error_free: + dctool_output_deallocate ((dctool_output_t *) output); +error_exit: + return NULL; +} + +static dc_status_t +dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const unsigned char data[], unsigned int size, const unsigned char fingerprint[], unsigned int fsize) +{ + dctool_xml_output_t *output = (dctool_xml_output_t *) abstract; + dc_status_t status = DC_STATUS_SUCCESS; + + fprintf (output->ostream, "\n%u\n%u\n", abstract->number, size); + + if (fingerprint) { + fprintf (output->ostream, ""); + for (unsigned int i = 0; i < fsize; ++i) + fprintf (output->ostream, "%02X", fingerprint[i]); + fprintf (output->ostream, "\n"); + } + + // Parse the datetime. + message ("Parsing the datetime.\n"); + dc_datetime_t dt = {0}; + status = dc_parser_get_datetime (parser, &dt); + if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the datetime."); + goto cleanup; + } + + fprintf (output->ostream, "%04i-%02i-%02i %02i:%02i:%02i\n", + dt.year, dt.month, dt.day, + dt.hour, dt.minute, dt.second); + + // Parse the divetime. + message ("Parsing the divetime.\n"); + unsigned int divetime = 0; + status = dc_parser_get_field (parser, DC_FIELD_DIVETIME, 0, &divetime); + if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the divetime."); + goto cleanup; + } + + fprintf (output->ostream, "%02u:%02u\n", + divetime / 60, divetime % 60); + + // Parse the maxdepth. + message ("Parsing the maxdepth.\n"); + double maxdepth = 0.0; + status = dc_parser_get_field (parser, DC_FIELD_MAXDEPTH, 0, &maxdepth); + if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the maxdepth."); + goto cleanup; + } + + fprintf (output->ostream, "%.2f\n", + maxdepth); + + // Parse the temperature. + message ("Parsing the temperature.\n"); + for (unsigned int i = 0; i < 3; ++i) { + dc_field_type_t fields[] = {DC_FIELD_TEMPERATURE_SURFACE, + DC_FIELD_TEMPERATURE_MINIMUM, + DC_FIELD_TEMPERATURE_MAXIMUM}; + const char *names[] = {"surface", "minimum", "maximum"}; + + double temperature = 0.0; + status = dc_parser_get_field (parser, fields[i], 0, &temperature); + if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the temperature."); + goto cleanup; + } + + if (status != DC_STATUS_UNSUPPORTED) { + fprintf (output->ostream, "%.1f\n", + names[i], temperature); + } + } + + // Parse the gas mixes. + message ("Parsing the gas mixes.\n"); + unsigned int ngases = 0; + status = dc_parser_get_field (parser, DC_FIELD_GASMIX_COUNT, 0, &ngases); + if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the gas mix count."); + goto cleanup; + } + + for (unsigned int i = 0; i < ngases; ++i) { + dc_gasmix_t gasmix = {0}; + status = dc_parser_get_field (parser, DC_FIELD_GASMIX, i, &gasmix); + if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the gas mix."); + goto cleanup; + } + + fprintf (output->ostream, + "\n" + " %.1f\n" + " %.1f\n" + " %.1f\n" + "\n", + gasmix.helium * 100.0, + gasmix.oxygen * 100.0, + gasmix.nitrogen * 100.0); + } + + // Parse the tanks. + message ("Parsing the tanks.\n"); + unsigned int ntanks = 0; + status = dc_parser_get_field (parser, DC_FIELD_TANK_COUNT, 0, &ntanks); + if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the tank count."); + goto cleanup; + } + + for (unsigned int i = 0; i < ntanks; ++i) { + const char *names[] = {"none", "metric", "imperial"}; + + dc_tank_t tank = {0}; + status = dc_parser_get_field (parser, DC_FIELD_TANK, i, &tank); + if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the tank."); + goto cleanup; + } + + fprintf (output->ostream, "\n"); + if (tank.gasmix != DC_GASMIX_UNKNOWN) { + fprintf (output->ostream, + " %u\n", + tank.gasmix); + } + if (tank.type != DC_TANKVOLUME_NONE) { + fprintf (output->ostream, + " %s\n" + " %.1f\n" + " %.2f\n", + names[tank.type], tank.volume, tank.workpressure); + } + fprintf (output->ostream, + " %.2f\n" + " %.2f\n" + "\n", + tank.beginpressure, tank.endpressure); + } + + // Parse the dive mode. + message ("Parsing the dive mode.\n"); + dc_divemode_t divemode = DC_DIVEMODE_OC; + status = dc_parser_get_field (parser, DC_FIELD_DIVEMODE, 0, &divemode); + if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the dive mode."); + goto cleanup; + } + + if (status != DC_STATUS_UNSUPPORTED) { + const char *names[] = {"freedive", "gauge", "oc", "cc"}; + fprintf (output->ostream, "%s\n", + names[divemode]); + } + + // Parse the salinity. + message ("Parsing the salinity.\n"); + dc_salinity_t salinity = {DC_WATER_FRESH, 0.0}; + status = dc_parser_get_field (parser, DC_FIELD_SALINITY, 0, &salinity); + if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the salinity."); + goto cleanup; + } + + if (status != DC_STATUS_UNSUPPORTED) { + fprintf (output->ostream, "%.1f\n", + salinity.type, salinity.density); + } + + // Parse the atmospheric pressure. + message ("Parsing the atmospheric pressure.\n"); + double atmospheric = 0.0; + status = dc_parser_get_field (parser, DC_FIELD_ATMOSPHERIC, 0, &atmospheric); + if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the atmospheric pressure."); + goto cleanup; + } + + if (status != DC_STATUS_UNSUPPORTED) { + fprintf (output->ostream, "%.5f\n", + atmospheric); + } + + // Initialize the sample data. + sample_data_t sampledata = {0}; + sampledata.nsamples = 0; + sampledata.ostream = output->ostream; + + // Parse the sample data. + message ("Parsing the sample data.\n"); + status = dc_parser_samples_foreach (parser, sample_cb, &sampledata); + if (status != DC_STATUS_SUCCESS) { + ERROR ("Error parsing the sample data."); + goto cleanup; + } + + if (sampledata.nsamples) + fprintf (output->ostream, "\n"); + fprintf (output->ostream, "\n"); + +cleanup: + return status; +} + +static dc_status_t +dctool_xml_output_free (dctool_output_t *abstract) +{ + dctool_xml_output_t *output = (dctool_xml_output_t *) abstract; + + fprintf (output->ostream, "\n"); + + fclose (output->ostream); + + return DC_STATUS_SUCCESS; +} From 5d9ddafc41a7889119953ecba810c227f72c65f5 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 6 Feb 2016 15:37:27 +0100 Subject: [PATCH 3/5] Add support for the raw output format. The RAW output format exports each dive to a raw (binary) file. To output multiple files, the filename is interpreted as a template and should contain one or more placeholders. --- examples/Makefile.am | 1 + examples/output.h | 3 + examples/output_raw.c | 219 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 223 insertions(+) create mode 100644 examples/output_raw.c diff --git a/examples/Makefile.am b/examples/Makefile.am index 3ae96d5..4c1aa05 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -21,5 +21,6 @@ dctool_SOURCES = \ output-private.h \ output.c \ output_xml.c \ + output_raw.c \ utils.h \ utils.c diff --git a/examples/output.h b/examples/output.h index b123689..0912de2 100644 --- a/examples/output.h +++ b/examples/output.h @@ -34,6 +34,9 @@ typedef struct dctool_output_t dctool_output_t; dctool_output_t * dctool_xml_output_new (const char *filename); +dctool_output_t * +dctool_raw_output_new (const char *template); + dc_status_t dctool_output_write (dctool_output_t *output, dc_parser_t *parser, const unsigned char data[], unsigned int size, const unsigned char fingerprint[], unsigned int fsize); diff --git a/examples/output_raw.c b/examples/output_raw.c new file mode 100644 index 0000000..951531e --- /dev/null +++ b/examples/output_raw.c @@ -0,0 +1,219 @@ +/* + * libdivecomputer + * + * Copyright (C) 2016 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include +#include +#include + +#include "output-private.h" +#include "utils.h" + +static dc_status_t dctool_raw_output_write (dctool_output_t *output, dc_parser_t *parser, const unsigned char data[], unsigned int size, const unsigned char fingerprint[], unsigned int fsize); +static dc_status_t dctool_raw_output_free (dctool_output_t *output); + +typedef struct dctool_raw_output_t { + dctool_output_t base; + char *template; +} dctool_raw_output_t; + +static const dctool_output_vtable_t raw_vtable = { + sizeof(dctool_raw_output_t), /* size */ + dctool_raw_output_write, /* write */ + dctool_raw_output_free, /* free */ +}; + +static int +mktemplate_fingerprint (char *buffer, size_t size, const unsigned char fingerprint[], size_t fsize) +{ + const unsigned char ascii[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + if (size < 2 * fsize + 1) + return -1; + + for (size_t i = 0; i < fsize; ++i) { + // Set the most-significant nibble. + unsigned char msn = (fingerprint[i] >> 4) & 0x0F; + buffer[i * 2 + 0] = ascii[msn]; + + // Set the least-significant nibble. + unsigned char lsn = fingerprint[i] & 0x0F; + buffer[i * 2 + 1] = ascii[lsn]; + } + + // Null-terminate the string. + buffer[fsize * 2] = 0; + + return fsize * 2; +} + +static int +mktemplate_datetime (char *buffer, size_t size, dc_parser_t *parser) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + dc_datetime_t datetime = {0}; + int n = 0; + + rc = dc_parser_get_datetime (parser, &datetime); + if (rc != DC_STATUS_SUCCESS) + return -1; + + n = snprintf (buffer, size, "%04i%02i%02iT%02i%02i%02i", + datetime.year, datetime.month, datetime.day, + datetime.hour, datetime.minute, datetime.second); + if (n < 0 || n >= size) + return -1; + + return n; +} + +static int +mktemplate_number (char *buffer, size_t size, unsigned int number) +{ + int n = 0; + + n = snprintf (buffer, size, "%04u", number); + if (n < 0 || n >= size) + return -1; + + return n; +} + +static int +mktemplate (char *buffer, size_t size, const char *format, dc_parser_t *parser, const unsigned char fingerprint[], size_t fsize, unsigned int number) +{ + const char *p = format; + size_t n = 0; + int len = 0; + char ch = 0; + + while ((ch = *p++) != 0) { + if (ch != '%') { + if (n >= size) + return -1; + buffer[n] = ch; + n++; + continue; + } + + ch = *p++; + switch (ch) { + case '%': + if (n >= size) + return -1; + buffer[n] = ch; + n++; + break; + case 't': // Timestamp + len = mktemplate_datetime (buffer + n, size - n, parser); + if (len < 0) + return -1; + n += len; + break; + case 'f': // Fingerprint + len = mktemplate_fingerprint (buffer + n, size - n, fingerprint, fsize); + if (len < 0) + return -1; + n += len; + break; + case 'n': // Number + len = mktemplate_number (buffer + n, size - n, number); + if (len < 0) + return -1; + n += len; + break; + default: + return -1; + } + } + + // Null-terminate the string + if (n >= size) + return -1; + buffer[n] = 0; + + return n; +} + +dctool_output_t * +dctool_raw_output_new (const char *template) +{ + dctool_raw_output_t *output = NULL; + + if (template == NULL) + goto error_exit; + + // Allocate memory. + output = (dctool_raw_output_t *) dctool_output_allocate (&raw_vtable); + if (output == NULL) { + goto error_exit; + } + + output->template = strdup(template); + if (output->template == NULL) { + goto error_free; + } + + return (dctool_output_t *) output; + +error_free: + dctool_output_deallocate ((dctool_output_t *) output); +error_exit: + return NULL; +} + +static dc_status_t +dctool_raw_output_write (dctool_output_t *abstract, dc_parser_t *parser, const unsigned char data[], unsigned int size, const unsigned char fingerprint[], unsigned int fsize) +{ + dctool_raw_output_t *output = (dctool_raw_output_t *) abstract; + + // Generate the filename. + char name[1024] = {0}; + int ret = mktemplate (name, sizeof(name), output->template, parser, fingerprint, fsize, abstract->number); + if (ret < 0) { + ERROR("Failed to generate filename from template."); + return DC_STATUS_SUCCESS; + } + + // Open the output file. + FILE *fp = fopen (name, "wb"); + if (fp == NULL) { + ERROR("Failed to open the output file."); + return DC_STATUS_SUCCESS; + } + + // Write the data. + fwrite (data, sizeof (unsigned char), size, fp); + fclose (fp); + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +dctool_raw_output_free (dctool_output_t *abstract) +{ + dctool_raw_output_t *output = (dctool_raw_output_t *) abstract; + + free (output->template); + + return DC_STATUS_SUCCESS; +} From e8b9e881714b62e57c35320b3d58beb8b0914de8 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 6 Feb 2016 15:58:23 +0100 Subject: [PATCH 4/5] Integrate the new output formats. The existing output code is removed and replaced with the new XML and RAW output formats. The desired output format can be selected with a new command-line option. The XML format remains the default output format. --- examples/dctool_download.c | 380 +++++++------------------------------ 1 file changed, 70 insertions(+), 310 deletions(-) diff --git a/examples/dctool_download.c b/examples/dctool_download.c index 61c4127..4baa333 100644 --- a/examples/dctool_download.c +++ b/examples/dctool_download.c @@ -38,6 +38,7 @@ #include "dctool.h" #include "common.h" +#include "output.h" #include "utils.h" typedef struct event_data_t { @@ -46,305 +47,18 @@ typedef struct event_data_t { } event_data_t; typedef struct dive_data_t { - FILE* ostream; dc_device_t *device; dc_buffer_t **fingerprint; unsigned int number; + dctool_output_t *output; } dive_data_t; -typedef struct sample_data_t { - FILE* ostream; - unsigned int nsamples; -} sample_data_t; - -static void -sample_cb (dc_sample_type_t type, dc_sample_value_t value, void *userdata) -{ - static const char *events[] = { - "none", "deco", "rbt", "ascent", "ceiling", "workload", "transmitter", - "violation", "bookmark", "surface", "safety stop", "gaschange", - "safety stop (voluntary)", "safety stop (mandatory)", "deepstop", - "ceiling (safety stop)", "floor", "divetime", "maxdepth", - "OLF", "PO2", "airtime", "rgbm", "heading", "tissue level warning", - "gaschange2"}; - static const char *decostop[] = { - "ndl", "safety", "deco", "deep"}; - - sample_data_t *sampledata = (sample_data_t *) userdata; - - switch (type) { - case DC_SAMPLE_TIME: - if (sampledata->nsamples++) - fprintf (sampledata->ostream, "\n"); - fprintf (sampledata->ostream, "\n"); - fprintf (sampledata->ostream, " \n", value.time / 60, value.time % 60); - break; - case DC_SAMPLE_DEPTH: - fprintf (sampledata->ostream, " %.2f\n", value.depth); - break; - case DC_SAMPLE_PRESSURE: - fprintf (sampledata->ostream, " %.2f\n", value.pressure.tank, value.pressure.value); - break; - case DC_SAMPLE_TEMPERATURE: - fprintf (sampledata->ostream, " %.2f\n", value.temperature); - break; - case DC_SAMPLE_EVENT: - if (value.event.type != SAMPLE_EVENT_GASCHANGE && value.event.type != SAMPLE_EVENT_GASCHANGE2) { - fprintf (sampledata->ostream, " %s\n", - value.event.type, value.event.time, value.event.flags, value.event.value, events[value.event.type]); - } - break; - case DC_SAMPLE_RBT: - fprintf (sampledata->ostream, " %u\n", value.rbt); - break; - case DC_SAMPLE_HEARTBEAT: - fprintf (sampledata->ostream, " %u\n", value.heartbeat); - break; - case DC_SAMPLE_BEARING: - fprintf (sampledata->ostream, " %u\n", value.bearing); - break; - case DC_SAMPLE_VENDOR: - fprintf (sampledata->ostream, " ", value.vendor.type, value.vendor.size); - for (unsigned int i = 0; i < value.vendor.size; ++i) - fprintf (sampledata->ostream, "%02X", ((unsigned char *) value.vendor.data)[i]); - fprintf (sampledata->ostream, "\n"); - break; - case DC_SAMPLE_SETPOINT: - fprintf (sampledata->ostream, " %.2f\n", value.setpoint); - break; - case DC_SAMPLE_PPO2: - fprintf (sampledata->ostream, " %.2f\n", value.ppo2); - break; - case DC_SAMPLE_CNS: - fprintf (sampledata->ostream, " %.1f\n", value.cns * 100.0); - break; - case DC_SAMPLE_DECO: - fprintf (sampledata->ostream, " %s\n", - value.deco.time, value.deco.depth, decostop[value.deco.type]); - break; - case DC_SAMPLE_GASMIX: - fprintf (sampledata->ostream, " %u\n", value.gasmix); - break; - default: - break; - } -} - -static dc_status_t -doparse (FILE *ostream, dc_device_t *device, const unsigned char data[], unsigned int size) -{ - dc_status_t rc = DC_STATUS_SUCCESS; - dc_parser_t *parser = NULL; - - // Create the parser. - message ("Creating the parser.\n"); - rc = dc_parser_new (&parser, device); - if (rc != DC_STATUS_SUCCESS) { - ERROR ("Error creating the parser."); - goto cleanup; - } - - // Register the data. - message ("Registering the data.\n"); - rc = dc_parser_set_data (parser, data, size); - if (rc != DC_STATUS_SUCCESS) { - ERROR ("Error registering the data."); - goto cleanup; - } - - // Parse the datetime. - message ("Parsing the datetime.\n"); - dc_datetime_t dt = {0}; - rc = dc_parser_get_datetime (parser, &dt); - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - ERROR ("Error parsing the datetime."); - goto cleanup; - } - - fprintf (ostream, "%04i-%02i-%02i %02i:%02i:%02i\n", - dt.year, dt.month, dt.day, - dt.hour, dt.minute, dt.second); - - // Parse the divetime. - message ("Parsing the divetime.\n"); - unsigned int divetime = 0; - rc = dc_parser_get_field (parser, DC_FIELD_DIVETIME, 0, &divetime); - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - ERROR ("Error parsing the divetime."); - goto cleanup; - } - - fprintf (ostream, "%02u:%02u\n", - divetime / 60, divetime % 60); - - // Parse the maxdepth. - message ("Parsing the maxdepth.\n"); - double maxdepth = 0.0; - rc = dc_parser_get_field (parser, DC_FIELD_MAXDEPTH, 0, &maxdepth); - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - ERROR ("Error parsing the maxdepth."); - goto cleanup; - } - - fprintf (ostream, "%.2f\n", - maxdepth); - - // Parse the temperature. - message ("Parsing the temperature.\n"); - for (unsigned int i = 0; i < 3; ++i) { - dc_field_type_t fields[] = {DC_FIELD_TEMPERATURE_SURFACE, - DC_FIELD_TEMPERATURE_MINIMUM, - DC_FIELD_TEMPERATURE_MAXIMUM}; - const char *names[] = {"surface", "minimum", "maximum"}; - - double temperature = 0.0; - rc = dc_parser_get_field (parser, fields[i], 0, &temperature); - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - ERROR ("Error parsing the temperature."); - goto cleanup; - } - - if (rc != DC_STATUS_UNSUPPORTED) { - fprintf (ostream, "%.1f\n", - names[i], temperature); - } - } - - // Parse the gas mixes. - message ("Parsing the gas mixes.\n"); - unsigned int ngases = 0; - rc = dc_parser_get_field (parser, DC_FIELD_GASMIX_COUNT, 0, &ngases); - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - ERROR ("Error parsing the gas mix count."); - goto cleanup; - } - - for (unsigned int i = 0; i < ngases; ++i) { - dc_gasmix_t gasmix = {0}; - rc = dc_parser_get_field (parser, DC_FIELD_GASMIX, i, &gasmix); - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - ERROR ("Error parsing the gas mix."); - goto cleanup; - } - - fprintf (ostream, - "\n" - " %.1f\n" - " %.1f\n" - " %.1f\n" - "\n", - gasmix.helium * 100.0, - gasmix.oxygen * 100.0, - gasmix.nitrogen * 100.0); - } - - // Parse the tanks. - message ("Parsing the tanks.\n"); - unsigned int ntanks = 0; - rc = dc_parser_get_field (parser, DC_FIELD_TANK_COUNT, 0, &ntanks); - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - ERROR ("Error parsing the tank count."); - goto cleanup; - } - - for (unsigned int i = 0; i < ntanks; ++i) { - const char *names[] = {"none", "metric", "imperial"}; - - dc_tank_t tank = {0}; - rc = dc_parser_get_field (parser, DC_FIELD_TANK, i, &tank); - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - ERROR ("Error parsing the tank."); - goto cleanup; - } - - fprintf (ostream, "\n"); - if (tank.gasmix != DC_GASMIX_UNKNOWN) { - fprintf (ostream, - " %u\n", - tank.gasmix); - } - if (tank.type != DC_TANKVOLUME_NONE) { - fprintf (ostream, - " %s\n" - " %.1f\n" - " %.2f\n", - names[tank.type], tank.volume, tank.workpressure); - } - fprintf (ostream, - " %.2f\n" - " %.2f\n" - "\n", - tank.beginpressure, tank.endpressure); - } - - // Parse the dive mode. - message ("Parsing the dive mode.\n"); - dc_divemode_t divemode = DC_DIVEMODE_OC; - rc = dc_parser_get_field (parser, DC_FIELD_DIVEMODE, 0, &divemode); - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - ERROR ("Error parsing the dive mode."); - goto cleanup; - } - - if (rc != DC_STATUS_UNSUPPORTED) { - const char *names[] = {"freedive", "gauge", "oc", "cc"}; - fprintf (ostream, "%s\n", - names[divemode]); - } - - // Parse the salinity. - message ("Parsing the salinity.\n"); - dc_salinity_t salinity = {DC_WATER_FRESH, 0.0}; - rc = dc_parser_get_field (parser, DC_FIELD_SALINITY, 0, &salinity); - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - ERROR ("Error parsing the salinity."); - goto cleanup; - } - - if (rc != DC_STATUS_UNSUPPORTED) { - fprintf (ostream, "%.1f\n", - salinity.type, salinity.density); - } - - // Parse the atmospheric pressure. - message ("Parsing the atmospheric pressure.\n"); - double atmospheric = 0.0; - rc = dc_parser_get_field (parser, DC_FIELD_ATMOSPHERIC, 0, &atmospheric); - if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { - ERROR ("Error parsing the atmospheric pressure."); - goto cleanup; - } - - if (rc != DC_STATUS_UNSUPPORTED) { - fprintf (ostream, "%.5f\n", - atmospheric); - } - - // Initialize the sample data. - sample_data_t sampledata = {0}; - sampledata.nsamples = 0; - sampledata.ostream = ostream; - - // Parse the sample data. - message ("Parsing the sample data.\n"); - rc = dc_parser_samples_foreach (parser, sample_cb, &sampledata); - if (rc != DC_STATUS_SUCCESS) { - ERROR ("Error parsing the sample data."); - goto cleanup; - } - - if (sampledata.nsamples) - fprintf (ostream, "\n"); - -cleanup: - dc_parser_destroy (parser); - return rc; -} - static int dive_cb (const unsigned char *data, unsigned int size, const unsigned char *fingerprint, unsigned int fsize, void *userdata) { dive_data_t *divedata = (dive_data_t *) userdata; + dc_status_t rc = DC_STATUS_SUCCESS; + dc_parser_t *parser = NULL; divedata->number++; @@ -362,15 +76,32 @@ dive_cb (const unsigned char *data, unsigned int size, const unsigned char *fing *divedata->fingerprint = fp; } - fprintf (divedata->ostream, "\n%u\n%u\n", divedata->number, size); - for (unsigned int i = 0; i < fsize; ++i) - fprintf (divedata->ostream, "%02X", fingerprint[i]); - fprintf (divedata->ostream, "\n"); + // Create the parser. + message ("Creating the parser.\n"); + rc = dc_parser_new (&parser, divedata->device); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error creating the parser."); + goto cleanup; + } - doparse (divedata->ostream, divedata->device, data, size); + // Register the data. + message ("Registering the data.\n"); + rc = dc_parser_set_data (parser, data, size); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error registering the data."); + goto cleanup; + } - fprintf (divedata->ostream, "\n"); + // Parse the dive data. + message ("Parsing the dive data.\n"); + rc = dctool_output_write (divedata->output, parser, data, size, fingerprint, fsize); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error parsing the dive data."); + goto cleanup; + } +cleanup: + dc_parser_destroy (parser); return 1; } @@ -421,7 +152,7 @@ event_cb (dc_device_t *device, dc_event_type_t event, const void *data, void *us } static dc_status_t -download (dc_context_t *context, dc_descriptor_t *descriptor, const char *devname, const char *cachedir, dc_buffer_t *fingerprint, FILE *ostream) +download (dc_context_t *context, dc_descriptor_t *descriptor, const char *devname, const char *cachedir, dc_buffer_t *fingerprint, dctool_output_t *output) { dc_status_t rc = DC_STATUS_SUCCESS; dc_device_t *device = NULL; @@ -476,11 +207,9 @@ download (dc_context_t *context, dc_descriptor_t *descriptor, const char *devnam // Initialize the dive data. dive_data_t divedata = {0}; divedata.device = device; - divedata.ostream = ostream; divedata.fingerprint = &ofingerprint; divedata.number = 0; - - fprintf (ostream, "\n"); + divedata.output = output; // Download the dives. message ("Downloading the dives.\n"); @@ -490,8 +219,6 @@ download (dc_context_t *context, dc_descriptor_t *descriptor, const char *devnam goto cleanup; } - fprintf (ostream, "\n"); - // Store the fingerprint data. if (cachedir && ofingerprint) { char filename[1024] = {0}; @@ -518,23 +245,25 @@ dctool_download_run (int argc, char *argv[], dc_context_t *context, dc_descripto int exitcode = EXIT_SUCCESS; dc_status_t status = DC_STATUS_SUCCESS; dc_buffer_t *fingerprint = NULL; - FILE *ostream = NULL; + dctool_output_t *output = NULL; // Default option values. unsigned int help = 0; const char *fphex = NULL; const char *filename = NULL; const char *cachedir = NULL; + const char *format = "xml"; // Parse the command-line options. int opt = 0; - const char *optstring = "ho:p:c:"; + const char *optstring = "ho:p:c:f:"; #ifdef HAVE_GETOPT_LONG struct option options[] = { {"help", no_argument, 0, 'h'}, {"output", required_argument, 0, 'o'}, {"fingerprint", required_argument, 0, 'p'}, {"cache", required_argument, 0, 'c'}, + {"format", required_argument, 0, 'f'}, {0, 0, 0, 0 } }; while ((opt = getopt_long (argc, argv, optstring, options, NULL)) != -1) { @@ -554,6 +283,9 @@ dctool_download_run (int argc, char *argv[], dc_context_t *context, dc_descripto case 'c': cachedir = optarg; break; + case 'f': + format = optarg; + break; default: return EXIT_FAILURE; } @@ -571,16 +303,24 @@ dctool_download_run (int argc, char *argv[], dc_context_t *context, dc_descripto // Convert the fingerprint to binary. fingerprint = dctool_convert_hex2bin (fphex); - // Open the output file. - ostream = fopen (filename, "w"); - if (ostream == NULL) { - message ("Failed to open the output file.\n"); + // Create the output. + if (strcasecmp(format, "raw") == 0) { + output = dctool_raw_output_new (filename); + } else if (strcasecmp(format, "xml") == 0) { + output = dctool_xml_output_new (filename); + } else { + message ("Unknown output format: %s\n", format); + exitcode = EXIT_FAILURE; + goto cleanup; + } + if (output == NULL) { + message ("Failed to create the output.\n"); exitcode = EXIT_FAILURE; goto cleanup; } // Download the dives. - status = download (context, descriptor, argv[0], cachedir, fingerprint, ostream); + status = download (context, descriptor, argv[0], cachedir, fingerprint, output); if (status != DC_STATUS_SUCCESS) { message ("ERROR: %s\n", dctool_errmsg (status)); exitcode = EXIT_FAILURE; @@ -588,7 +328,7 @@ dctool_download_run (int argc, char *argv[], dc_context_t *context, dc_descripto } cleanup: - if (ostream) fclose (ostream); + dctool_output_free (output); dc_buffer_free (fingerprint); return exitcode; } @@ -607,10 +347,30 @@ const dctool_command_t dctool_download = { " -o, --output Output filename\n" " -p, --fingerprint Fingerprint data (hexadecimal)\n" " -c, --cache Cache directory\n" + " -f, --format Output format\n" #else " -h Show help message\n" " -o Output filename\n" " -p Fingerprint data (hexadecimal)\n" " -c Cache directory\n" + " -f Output format\n" #endif + "\n" + "Supported output formats:\n" + "\n" + " XML (default)\n" + "\n" + " All dives are exported to a single xml file.\n" + "\n" + " RAW\n" + "\n" + " Each dive is exported to a raw (binary) file. To output multiple\n" + " files, the filename is interpreted as a template and should\n" + " contain one or more placeholders.\n" + "\n" + "Supported template placeholders:\n" + "\n" + " %f Fingerprint (hexadecimal format)\n" + " %n Number (4 digits)\n" + " %t Timestamp (basic ISO 8601 date/time format)\n" }; From 83e54d84a212298b73d9a4db701462c2ca71a27c Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sun, 7 Feb 2016 21:29:27 +0100 Subject: [PATCH 5/5] Add support for the parse command. --- examples/Makefile.am | 1 + examples/dctool.c | 1 + examples/dctool.h | 1 + examples/dctool_parse.c | 304 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 307 insertions(+) create mode 100644 examples/dctool_parse.c diff --git a/examples/Makefile.am b/examples/Makefile.am index 4c1aa05..e830769 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -14,6 +14,7 @@ dctool_SOURCES = \ dctool_list.c \ dctool_download.c \ dctool_dump.c \ + dctool_parse.c \ dctool_read.c \ dctool_write.c \ dctool_fwupdate.c \ diff --git a/examples/dctool.c b/examples/dctool.c index a317980..b1c9e0f 100644 --- a/examples/dctool.c +++ b/examples/dctool.c @@ -60,6 +60,7 @@ static const dctool_command_t *g_commands[] = { &dctool_list, &dctool_download, &dctool_dump, + &dctool_parse, &dctool_read, &dctool_write, &dctool_fwupdate, diff --git a/examples/dctool.h b/examples/dctool.h index a0e09d7..cc6a393 100644 --- a/examples/dctool.h +++ b/examples/dctool.h @@ -47,6 +47,7 @@ extern const dctool_command_t dctool_version; extern const dctool_command_t dctool_list; extern const dctool_command_t dctool_download; extern const dctool_command_t dctool_dump; +extern const dctool_command_t dctool_parse; extern const dctool_command_t dctool_read; extern const dctool_command_t dctool_write; extern const dctool_command_t dctool_fwupdate; diff --git a/examples/dctool_parse.c b/examples/dctool_parse.c new file mode 100644 index 0000000..ef05aa4 --- /dev/null +++ b/examples/dctool_parse.c @@ -0,0 +1,304 @@ +/* + * libdivecomputer + * + * Copyright (C) 2015 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#ifdef HAVE_GETOPT_H +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dctool.h" +#include "output.h" +#include "common.h" +#include "utils.h" + +#define REACTPROWHITE 0x4354 + +static dc_status_t +parse (dc_buffer_t *buffer, dc_context_t *context, dc_descriptor_t *descriptor, unsigned int devtime, dc_ticks_t systime, dctool_output_t *output) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + dc_parser_t *parser = NULL; + dc_family_t family = dc_descriptor_get_type (descriptor); + unsigned int model = dc_descriptor_get_model (descriptor); + unsigned char *data = dc_buffer_get_data (buffer); + unsigned int size = dc_buffer_get_size (buffer); + + // Create the parser. + message ("Creating the parser.\n"); + switch (family) { + case DC_FAMILY_SUUNTO_SOLUTION: + rc = suunto_solution_parser_create (&parser, context); + break; + case DC_FAMILY_SUUNTO_EON: + rc = suunto_eon_parser_create (&parser, context, 0); + break; + case DC_FAMILY_SUUNTO_VYPER: + if (model == 0x01) + rc = suunto_eon_parser_create (&parser, context, 1); + else + rc = suunto_vyper_parser_create (&parser, context); + break; + case DC_FAMILY_SUUNTO_VYPER2: + case DC_FAMILY_SUUNTO_D9: + rc = suunto_d9_parser_create (&parser, context, model); + break; + case DC_FAMILY_SUUNTO_EONSTEEL: + rc = suunto_eonsteel_parser_create(&parser, context, model); + break; + case DC_FAMILY_UWATEC_ALADIN: + case DC_FAMILY_UWATEC_MEMOMOUSE: + rc = uwatec_memomouse_parser_create (&parser, context, devtime, systime); + break; + case DC_FAMILY_UWATEC_SMART: + case DC_FAMILY_UWATEC_MERIDIAN: + rc = uwatec_smart_parser_create (&parser, context, model, devtime, systime); + break; + case DC_FAMILY_REEFNET_SENSUS: + rc = reefnet_sensus_parser_create (&parser, context, devtime, systime); + break; + case DC_FAMILY_REEFNET_SENSUSPRO: + rc = reefnet_sensuspro_parser_create (&parser, context, devtime, systime); + break; + case DC_FAMILY_REEFNET_SENSUSULTRA: + rc = reefnet_sensusultra_parser_create (&parser, context, devtime, systime); + break; + case DC_FAMILY_OCEANIC_VTPRO: + rc = oceanic_vtpro_parser_create (&parser, context); + break; + case DC_FAMILY_OCEANIC_VEO250: + rc = oceanic_veo250_parser_create (&parser, context, model); + break; + case DC_FAMILY_OCEANIC_ATOM2: + if (model == REACTPROWHITE) + rc = oceanic_veo250_parser_create (&parser, context, model); + else + rc = oceanic_atom2_parser_create (&parser, context, model); + break; + case DC_FAMILY_MARES_NEMO: + case DC_FAMILY_MARES_PUCK: + rc = mares_nemo_parser_create (&parser, context, model); + break; + case DC_FAMILY_MARES_DARWIN: + rc = mares_darwin_parser_create (&parser, context, model); + break; + case DC_FAMILY_MARES_ICONHD: + rc = mares_iconhd_parser_create (&parser, context, model); + break; + case DC_FAMILY_HW_OSTC: + rc = hw_ostc_parser_create (&parser, context, 0); + break; + case DC_FAMILY_HW_FROG: + case DC_FAMILY_HW_OSTC3: + rc = hw_ostc_parser_create (&parser, context, 1); + break; + case DC_FAMILY_CRESSI_EDY: + case DC_FAMILY_ZEAGLE_N2ITION3: + rc = cressi_edy_parser_create (&parser, context, model); + break; + case DC_FAMILY_CRESSI_LEONARDO: + rc = cressi_leonardo_parser_create (&parser, context); + break; + case DC_FAMILY_ATOMICS_COBALT: + rc = atomics_cobalt_parser_create (&parser, context); + break; + case DC_FAMILY_SHEARWATER_PREDATOR: + rc = shearwater_predator_parser_create (&parser, context); + break; + case DC_FAMILY_SHEARWATER_PETREL: + rc = shearwater_petrel_parser_create (&parser, context); + break; + case DC_FAMILY_DIVERITE_NITEKQ: + rc = diverite_nitekq_parser_create (&parser, context); + break; + case DC_FAMILY_CITIZEN_AQUALAND: + rc = citizen_aqualand_parser_create (&parser, context); + break; + case DC_FAMILY_DIVESYSTEM_IDIVE: + rc = divesystem_idive_parser_create2 (&parser, context, model); + break; + default: + rc = DC_STATUS_INVALIDARGS; + break; + } + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error creating the parser."); + goto cleanup; + } + + // Register the data. + message ("Registering the data.\n"); + rc = dc_parser_set_data (parser, data, size); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error registering the data."); + goto cleanup; + } + + // Parse the dive data. + message ("Parsing the dive data.\n"); + rc = dctool_output_write (output, parser, data, size, NULL, 0); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error parsing the dive data."); + goto cleanup; + } + +cleanup: + dc_parser_destroy (parser); + return rc; +} + +static int +dctool_parse_run (int argc, char *argv[], dc_context_t *context, dc_descriptor_t *descriptor) +{ + // Default values. + int exitcode = EXIT_SUCCESS; + dc_status_t status = DC_STATUS_SUCCESS; + dc_buffer_t *buffer = NULL; + dctool_output_t *output = NULL; + + // Default option values. + unsigned int help = 0; + const char *filename = NULL; + unsigned int devtime = 0; + dc_ticks_t systime = 0; + + // Parse the command-line options. + int opt = 0; + const char *optstring = "ho:d:s:"; +#ifdef HAVE_GETOPT_LONG + struct option options[] = { + {"help", no_argument, 0, 'h'}, + {"output", required_argument, 0, 'o'}, + {"devtime", required_argument, 0, 'd'}, + {"systime", required_argument, 0, 's'}, + {0, 0, 0, 0 } + }; + while ((opt = getopt_long (argc, argv, optstring, options, NULL)) != -1) { +#else + while ((opt = getopt (argc, argv, optstring)) != -1) { +#endif + switch (opt) { + case 'h': + help = 1; + break; + case 'o': + filename = optarg; + break; + case 'd': + devtime = strtoul (optarg, NULL, 0); + break; + case 's': + systime = strtoll (optarg, NULL, 0); + break; + default: + return EXIT_FAILURE; + } + } + + argc -= optind; + argv += optind; + + // Show help message. + if (help) { + dctool_command_showhelp (&dctool_parse); + return EXIT_SUCCESS; + } + + // Create the output. + output = dctool_xml_output_new (filename); + if (output == NULL) { + message ("Failed to create the output.\n"); + exitcode = EXIT_FAILURE; + goto cleanup; + } + + for (unsigned int i = 0; i < argc; ++i) { + // Read the input file. + buffer = dctool_file_read (argv[i]); + if (buffer == NULL) { + message ("Failed to open the input file.\n"); + exitcode = EXIT_FAILURE; + goto cleanup; + } + + // Parse the dive. + status = parse (buffer, context, descriptor, devtime, systime, output); + if (status != DC_STATUS_SUCCESS) { + message ("ERROR: %s\n", dctool_errmsg (status)); + exitcode = EXIT_FAILURE; + goto cleanup; + } + + // Cleanup. + dc_buffer_free (buffer); + buffer = NULL; + } + +cleanup: + dc_buffer_free (buffer); + dctool_output_free (output); + return exitcode; +} + +const dctool_command_t dctool_parse = { + dctool_parse_run, + DCTOOL_CONFIG_DESCRIPTOR, + "parse", + "Parse previously downloaded dives", + "Usage:\n" + " dctool parse [options] \n" + "\n" + "Options:\n" +#ifdef HAVE_GETOPT_LONG + " -h, --help Show help message\n" + " -o, --output Output filename\n" + " -d, --devtime Device time\n" + " -s, --systime System time\n" +#else + " -h Show help message\n" + " -o Output filename\n" + " -d Device time\n" + " -s System time\n" +#endif +};