Jef Driesen c8b2d89354 Set the default model number.
When the device family is provided without an explicit model number, we
simply choose the first available model. But since new models are being
added all the time, this default model is not guaranteed to remain the
same. That's not desirable because it can alter the behaviour of the
application.

The introduction of the Aeris 500AI is an example of this problem. The
default model in the vtpro family used to be the Oceanic Versa Pro. But
because the Aeris 500AI has a lower model number, it automatically
became the new default model. Since both use a different protocol
variant (MOD vs INTR) they are not interchangable.

The default model is now hardcoded. The best option is of course to
provide the model number explicitly!
2016-07-11 21:49:16 +02:00

315 lines
7.5 KiB
C

/*
* 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 <string.h>
#include <stdio.h>
#include <signal.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#include <libdivecomputer/context.h>
#include <libdivecomputer/descriptor.h>
#include "common.h"
#include "dctool.h"
#include "utils.h"
#if defined(__GLIBC__) || defined(__MINGW32__)
#define RESET 0
#else
#define RESET 1
#endif
#if defined(__GLIBC__) || defined(__MINGW32__) || defined(BSD)
#define NOPERMUTATION "+"
#else
#define NOPERMUTATION ""
#endif
static const dctool_command_t *g_commands[] = {
&dctool_help,
&dctool_version,
&dctool_list,
&dctool_download,
&dctool_dump,
&dctool_parse,
&dctool_read,
&dctool_write,
&dctool_fwupdate,
NULL
};
static volatile sig_atomic_t g_cancel = 0;
const dctool_command_t *
dctool_command_find (const char *name)
{
if (name == NULL)
return NULL;
size_t i = 0;
while (g_commands[i] != NULL) {
if (strcmp(g_commands[i]->name, name) == 0) {
break;
}
i++;
}
return g_commands[i];
}
void
dctool_command_showhelp (const dctool_command_t *command)
{
if (command == NULL) {
unsigned int maxlength = 0;
for (size_t i = 0; g_commands[i] != NULL; ++i) {
unsigned int length = strlen (g_commands[i]->name);
if (length > maxlength)
maxlength = length;
}
printf (
"A simple command line interface for the libdivecomputer library\n"
"\n"
"Usage:\n"
" dctool [options] <command> [<args>]\n"
"\n"
"Options:\n"
#ifdef HAVE_GETOPT_LONG
" -h, --help Show help message\n"
" -d, --device <device> Device name\n"
" -f, --family <family> Device family type\n"
" -m, --model <model> Device model number\n"
" -l, --logfile <logfile> Logfile\n"
" -q, --quiet Quiet mode\n"
" -v, --verbose Verbose mode\n"
#else
" -h Show help message\n"
" -d <device> Device name\n"
" -f <family> Family type\n"
" -m <model> Model number\n"
" -l <logfile> Logfile\n"
" -q Quiet mode\n"
" -v Verbose mode\n"
#endif
"\n"
"Available commands:\n");
for (size_t i = 0; g_commands[i] != NULL; ++i) {
printf (" %-*s%s\n", maxlength + 3, g_commands[i]->name, g_commands[i]->description);
}
printf ("\nSee 'dctool help <command>' for more information on a specific command.\n\n");
} else {
printf ("%s\n\n%s\n", command->description, command->usage);
}
}
int
dctool_cancel_cb (void *userdata)
{
return g_cancel;
}
static void
sighandler (int signum)
{
#ifndef _WIN32
// Restore the default signal handler.
signal (signum, SIG_DFL);
#endif
g_cancel = 1;
}
static void
logfunc (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *msg, void *userdata)
{
const char *loglevels[] = {"NONE", "ERROR", "WARNING", "INFO", "DEBUG", "ALL"};
if (loglevel == DC_LOGLEVEL_ERROR || loglevel == DC_LOGLEVEL_WARNING) {
message ("%s: %s [in %s:%d (%s)]\n", loglevels[loglevel], msg, file, line, function);
} else {
message ("%s: %s\n", loglevels[loglevel], msg);
}
}
int
main (int argc, char *argv[])
{
int exitcode = EXIT_SUCCESS;
dc_status_t status = DC_STATUS_SUCCESS;
dc_context_t *context = NULL;
dc_descriptor_t *descriptor = NULL;
// Default option values.
unsigned int help = 0;
dc_loglevel_t loglevel = DC_LOGLEVEL_WARNING;
const char *logfile = NULL;
const char *device = NULL;
dc_family_t family = DC_FAMILY_NULL;
unsigned int model = 0;
unsigned int have_family = 0, have_model = 0;
// Parse the command-line options.
int opt = 0;
const char *optstring = NOPERMUTATION "hd:f:m:l:qv";
#ifdef HAVE_GETOPT_LONG
struct option options[] = {
{"help", no_argument, 0, 'h'},
{"device", required_argument, 0, 'd'},
{"family", required_argument, 0, 'f'},
{"model", required_argument, 0, 'm'},
{"logfile", required_argument, 0, 'l'},
{"quiet", no_argument, 0, 'q'},
{"verbose", no_argument, 0, 'v'},
{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 'd':
device = optarg;
break;
case 'f':
family = dctool_family_type (optarg);
have_family = 1;
break;
case 'm':
model = strtoul (optarg, NULL, 0);
have_model = 1;
break;
case 'l':
logfile = optarg;
break;
case 'q':
loglevel = DC_LOGLEVEL_NONE;
break;
case 'v':
loglevel++;
break;
default:
return EXIT_FAILURE;
}
}
// Skip the processed arguments.
argc -= optind;
argv += optind;
optind = RESET;
#if defined(HAVE_DECL_OPTRESET) && HAVE_DECL_OPTRESET
optreset = 1;
#endif
// Set the default model number.
if (have_family && !have_model) {
model = dctool_family_model (family);
}
// Translate the help option into a command.
char *argv_help[] = {(char *) "help", NULL, NULL};
if (help || argv[0] == NULL) {
if (argv[0]) {
argv_help[1] = argv[0];
argv = argv_help;
argc = 2;
} else {
argv = argv_help;
argc = 1;
}
}
// Try to find the command.
const dctool_command_t *command = dctool_command_find (argv[0]);
if (command == NULL) {
message ("Unknown command %s.\n", argv[0]);
return EXIT_FAILURE;
}
// Setup the cancel signal handler.
signal (SIGINT, sighandler);
// Initialize the logfile.
message_set_logfile (logfile);
// Initialize a library context.
status = dc_context_new (&context);
if (status != DC_STATUS_SUCCESS) {
exitcode = EXIT_FAILURE;
goto cleanup;
}
// Setup the logging.
dc_context_set_loglevel (context, loglevel);
dc_context_set_logfunc (context, logfunc, NULL);
if (command->config & DCTOOL_CONFIG_DESCRIPTOR) {
// Check mandatory arguments.
if (device == NULL && family == DC_FAMILY_NULL) {
message ("No device name or family type specified.\n");
exitcode = EXIT_FAILURE;
goto cleanup;
}
// Search for a matching device descriptor.
status = dctool_descriptor_search (&descriptor, device, family, model);
if (status != DC_STATUS_SUCCESS) {
exitcode = EXIT_FAILURE;
goto cleanup;
}
// Fail if no device descriptor found.
if (descriptor == NULL) {
if (device) {
message ("No supported device found: %s\n",
device);
} else {
message ("No supported device found: %s, 0x%X\n",
dctool_family_name (family), model);
}
exitcode = EXIT_FAILURE;
goto cleanup;
}
}
// Execute the command.
exitcode = command->run (argc, argv, context, descriptor);
cleanup:
dc_descriptor_free (descriptor);
dc_context_free (context);
message_set_logfile (NULL);
return exitcode;
}