[PATCH 3/3] Added "extract" command to dctool.
John Van Ostrand
john at vanostrand.com
Thu Feb 4 12:33:57 PST 2016
An extract command was added to dctool to store a DC's individual
dive data into files. This is helpful for developing parsing
algorithms without having to wait for a slow DC or simulate a DC.
---
examples/Makefile.am | 1 +
examples/dctool.c | 1 +
examples/dctool.h | 1 +
examples/dctool_extract.c | 357 ++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 360 insertions(+)
create mode 100644 examples/dctool_extract.c
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 12bca6f..b1e1e42 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -18,5 +18,6 @@ dctool_SOURCES = \
dctool_read.c \
dctool_write.c \
dctool_fwupdate.c \
+ dctool_extract.c \
utils.h \
utils.c
diff --git a/examples/dctool.c b/examples/dctool.c
index 4d70c0e..127352e 100644
--- a/examples/dctool.c
+++ b/examples/dctool.c
@@ -56,6 +56,7 @@ static const dctool_command_t *g_commands[] = {
&dctool_read,
&dctool_write,
&dctool_fwupdate,
+ &dctool_extract,
NULL
};
diff --git a/examples/dctool.h b/examples/dctool.h
index a0e09d7..6ee58a9 100644
--- a/examples/dctool.h
+++ b/examples/dctool.h
@@ -50,6 +50,7 @@ extern const dctool_command_t dctool_dump;
extern const dctool_command_t dctool_read;
extern const dctool_command_t dctool_write;
extern const dctool_command_t dctool_fwupdate;
+extern const dctool_command_t dctool_extract;
const dctool_command_t *
dctool_command_find (const char *name);
diff --git a/examples/dctool_extract.c b/examples/dctool_extract.c
new file mode 100644
index 0000000..4ed604e
--- /dev/null
+++ b/examples/dctool_extract.c
@@ -0,0 +1,357 @@
+/*
+ * 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 <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+
+#include <libdivecomputer/context.h>
+#include <libdivecomputer/descriptor.h>
+#include <libdivecomputer/device.h>
+#include <libdivecomputer/parser.h>
+
+#include "dctool.h"
+#include "common.h"
+#include "utils.h"
+
+typedef struct event_data_t {
+ const char *cachedir;
+ dc_event_devinfo_t devinfo;
+} event_data_t;
+
+typedef struct dive_data_t {
+ const char *path;
+ dc_device_t *device;
+ dc_buffer_t **fingerprint;
+ unsigned int number;
+} dive_data_t;
+
+
+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;
+
+ divedata->number++;
+
+ char *fp_string = malloc(fsize * 0 + 1);
+ if (!fp_string) {
+ message ("Unable to allocate memory in dive_cb\n");
+ return 0;
+ }
+
+ message ("Dive: number=%u, size=%u, fingerprint=", divedata->number, size);
+ for (unsigned int i = 0; i < fsize; ++i) {
+ message ("%02X", fingerprint[i]);
+ sprintf(fp_string + i * 2, "%02X", fingerprint[i]);
+ }
+ message ("\n");
+
+ // Keep a copy of the most recent fingerprint. Because dives are
+ // guaranteed to be downloaded in reverse order, the most recent
+ // dive is always the first dive.
+ if (divedata->number == 1) {
+ dc_buffer_t *fp = dc_buffer_new (fsize);
+ dc_buffer_append (fp, fingerprint, fsize);
+ *divedata->fingerprint = fp;
+ }
+
+ // Write out dive into new file.
+ char *path = malloc(strlen(divedata->path) + (fsize * 2) + 2);
+ sprintf(path, "%s/%s", divedata->path, fp_string);
+ free(fp_string);
+
+ int fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ free(path);
+
+ if (fd == -1) {
+ message ("ERROR: %s creating file %s\n", strerror(errno), path);
+ return 0;
+ }
+
+ int bytes_written = write(fd, data, size);
+
+ if (bytes_written == -1) {
+ message ("ERROR: %s while writing %s\n", strerror(errno), path);
+ return 0;
+ } else if (bytes_written != size) {
+ message ("ERROR: unable to write %s\n", path);
+ }
+
+ return 1;
+}
+
+static void
+event_cb (dc_device_t *device, dc_event_type_t event, const void *data, void *userdata)
+{
+ const dc_event_devinfo_t *devinfo = (const dc_event_devinfo_t *) data;
+
+ event_data_t *eventdata = (event_data_t *) userdata;
+
+ // Forward to the default event handler.
+ dctool_event_cb (device, event, data, userdata);
+
+ switch (event) {
+ case DC_EVENT_DEVINFO:
+ // Load the fingerprint from the cache. If there is no
+ // fingerprint present in the cache, a NULL buffer is returned,
+ // and the registered fingerprint will be cleared.
+ if (eventdata->cachedir) {
+ char filename[1024] = {0};
+ dc_family_t family = DC_FAMILY_NULL;
+ dc_buffer_t *fingerprint = NULL;
+
+ // Generate the fingerprint filename.
+ family = dc_device_get_type (device);
+ snprintf (filename, sizeof (filename), "%s/%s-%08X.bin",
+ eventdata->cachedir, dctool_family_name (family), devinfo->serial);
+
+ // Read the fingerprint file.
+ fingerprint = dctool_file_read (filename);
+
+ // Register the fingerprint data.
+ dc_device_set_fingerprint (device,
+ dc_buffer_get_data (fingerprint),
+ dc_buffer_get_size (fingerprint));
+
+ // Free the buffer again.
+ dc_buffer_free (fingerprint);
+ }
+
+ // Keep a copy of the event data. It will be used for generating
+ // the fingerprint filename again after a (successful) extract.
+ eventdata->devinfo = *devinfo;
+ break;
+ default:
+ break;
+ }
+}
+
+static dc_status_t
+extract (dc_context_t *context, dc_descriptor_t *descriptor, const char *devname, const char *cachedir, dc_buffer_t *fingerprint, const char *path)
+{
+ dc_status_t rc = DC_STATUS_SUCCESS;
+ dc_device_t *device = NULL;
+ dc_buffer_t *ofingerprint = NULL;
+
+ // Open the device.
+ message ("Opening the device (%s %s, %s).\n",
+ dc_descriptor_get_vendor (descriptor),
+ dc_descriptor_get_product (descriptor),
+ devname ? devname : "null");
+ rc = dc_device_open (&device, context, descriptor, devname);
+ if (rc != DC_STATUS_SUCCESS) {
+ ERROR ("Error opening the device.");
+ goto cleanup;
+ }
+
+ // Initialize the event data.
+ event_data_t eventdata = {0};
+ if (fingerprint) {
+ eventdata.cachedir = NULL;
+ } else {
+ eventdata.cachedir = cachedir;
+ }
+
+ // Register the event handler.
+ message ("Registering the event handler.\n");
+ int events = DC_EVENT_WAITING | DC_EVENT_PROGRESS | DC_EVENT_DEVINFO | DC_EVENT_CLOCK | DC_EVENT_VENDOR;
+ rc = dc_device_set_events (device, events, event_cb, &eventdata);
+ if (rc != DC_STATUS_SUCCESS) {
+ ERROR ("Error registering the event handler.");
+ goto cleanup;
+ }
+
+ // Register the cancellation handler.
+ message ("Registering the cancellation handler.\n");
+ rc = dc_device_set_cancel (device, dctool_cancel_cb, NULL);
+ if (rc != DC_STATUS_SUCCESS) {
+ ERROR ("Error registering the cancellation handler.");
+ goto cleanup;
+ }
+
+ // Register the fingerprint data.
+ if (fingerprint) {
+ message ("Registering the fingerprint data.\n");
+ rc = dc_device_set_fingerprint (device, dc_buffer_get_data (fingerprint), dc_buffer_get_size (fingerprint));
+ if (rc != DC_STATUS_SUCCESS) {
+ ERROR ("Error registering the fingerprint data.");
+ goto cleanup;
+ }
+ }
+
+ // Initialize the dive data.
+ dive_data_t divedata = {0};
+ divedata.device = device;
+ divedata.path = path;
+ divedata.fingerprint = &ofingerprint;
+ divedata.number = 0;
+
+ // Download the dives.
+ message ("Downloading the dives.\n");
+ rc = dc_device_foreach (device, dive_cb, &divedata);
+ if (rc != DC_STATUS_SUCCESS) {
+ ERROR ("Error extracting the dives.");
+ goto cleanup;
+ }
+
+ // Store the fingerprint data.
+ if (cachedir && ofingerprint) {
+ char filename[1024] = {0};
+ dc_family_t family = DC_FAMILY_NULL;
+
+ // Generate the fingerprint filename.
+ family = dc_device_get_type (device);
+ snprintf (filename, sizeof (filename), "%s/%s-%08X.bin",
+ cachedir, dctool_family_name (family), eventdata.devinfo.serial);
+
+ // Write the fingerprint file.
+ dctool_file_write (filename, ofingerprint);
+ }
+
+cleanup:
+ dc_buffer_free (ofingerprint);
+ dc_device_close (device);
+ return rc;
+}
+
+static int
+dctool_extract_run (int argc, char *argv[], dc_context_t *context, dc_descriptor_t *descriptor)
+{
+ int exitcode = EXIT_SUCCESS;
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_buffer_t *fingerprint = NULL;
+
+ // Default option values.
+ unsigned int help = 0;
+ const char *fphex = NULL;
+ const char *path = NULL;
+ const char *cachedir = NULL;
+
+ // Parse the command-line options.
+ int opt = 0;
+ const char *optstring = "ho:p:c:";
+#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'},
+ {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':
+ path = optarg;
+ break;
+ case 'p':
+ fphex = optarg;
+ break;
+ case 'c':
+ cachedir = optarg;
+ break;
+ default:
+ return EXIT_FAILURE;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ // Show help message.
+ if (help) {
+ dctool_command_showhelp (&dctool_extract);
+ return EXIT_SUCCESS;
+ }
+
+ // Convert the fingerprint to binary.
+ fingerprint = dctool_convert_hex2bin (fphex);
+
+ // Create directory if needed
+ struct stat ds;
+ if (stat(path, &ds)) {
+ // Assume it doesn't exist
+ if (mkdir(path, 0777)) {
+ message ("ERROR: %s while creating directory\n", strerror(errno));
+ exitcode = EXIT_FAILURE;
+ goto cleanup;
+ }
+ } else {
+ if (!(ds.st_mode && S_IFDIR)) {
+ message ("ERROR: Path given is not a directory\n");
+ exitcode = EXIT_FAILURE;
+ goto cleanup;
+ }
+ }
+
+ // Extract the dives.
+ status = extract (context, descriptor, argv[0], cachedir, fingerprint, path);
+ if (status != DC_STATUS_SUCCESS) {
+ message ("ERROR: %s\n", dctool_errmsg (status));
+ exitcode = EXIT_FAILURE;
+ goto cleanup;
+ }
+
+cleanup:
+ dc_buffer_free (fingerprint);
+ return exitcode;
+}
+
+const dctool_command_t dctool_extract = {
+ dctool_extract_run,
+ DCTOOL_CONFIG_DESCRIPTOR,
+ "extract",
+ "Extract dive data into files",
+ "Usage:\n"
+ " dctool extract [options] <devname>\n"
+ "\n"
+ "Options:\n"
+#ifdef HAVE_GETOPT_LONG
+ " -h, --help Show help message\n"
+ " -o, --output <dir> Directory to store dive data\n"
+ " -p, --fingerprint <data> Fingerprint data (hexadecimal)\n"
+ " -c, --cache <directory> Cache directory\n"
+#else
+ " -h Show help message\n"
+ " -o <dir> Directory to store dive data\n"
+ " -p <fingerprint> Fingerprint data (hexadecimal)\n"
+ " -c <directory> Cache directory\n"
+#endif
+};
--
2.4.3
More information about the devel
mailing list