[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