#include <string.h>
#include <stdlib.h>
#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
#define __USE_XOPEN
#include <time.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
+#ifdef XSLT
+#include <libxslt/transform.h>
+#endif
#include "dive.h"
#include "uemis.h"
static enum import_source {
UNKNOWN,
LIBDIVECOMPUTER,
- SUUNTO,
UEMIS,
DIVINGLOG,
UDDF,
if (!val.fp)
break;
/* cbar to atm */
- atm = (val.fp / 100) / 1.01325;
+ atm = bar_to_atm(val.fp * 10);
/*
* atm to cm. Why not mm? The precision just isn't
* there.
return;
if (MATCH(".sample.cylpress", pressure, &sample->cylinderpressure))
return;
+ if (MATCH(".sample.cylinderindex", get_index, &sample->cylinderindex))
+ return;
if (MATCH(".sample.depth", depth, &sample->depth))
return;
if (MATCH(".sample.temp", temperature, &sample->temperature))
nonmatch("sample", name, buf);
}
-/*
- * Crazy suunto xml. Look at how those o2/he things match up.
- */
-static int suunto_dive_match(struct dive **divep, const char *name, int len, char *buf)
-{
- struct dive *dive = *divep;
-
- return MATCH(".o2pct", percent, &dive->cylinder[0].gasmix.o2) ||
- MATCH(".hepct_0", percent, &dive->cylinder[0].gasmix.he) ||
- MATCH(".o2pct_2", percent, &dive->cylinder[1].gasmix.o2) ||
- MATCH(".hepct_1", percent, &dive->cylinder[1].gasmix.he) ||
- MATCH(".o2pct_3", percent, &dive->cylinder[2].gasmix.o2) ||
- MATCH(".hepct_2", percent, &dive->cylinder[2].gasmix.he) ||
- MATCH(".o2pct_4", percent, &dive->cylinder[3].gasmix.o2) ||
- MATCH(".hepct_3", percent, &dive->cylinder[3].gasmix.he) ||
- MATCH(".cylindersize", cylindersize, &dive->cylinder[0].type.size) ||
- MATCH(".cylinderworkpressure", pressure, &dive->cylinder[0].type.workingpressure) ||
- 0;
-}
-
static const char *country, *city;
static void divinglog_place(char *place, void *_location)
struct tm tm;
time_t *when = _when;
- strptime(buffer, "%Y-%m-%dT%H:%M:%S", &tm);
+ memset(&tm, 0, sizeof(tm));
+ sscanf(buffer,"%d-%d-%dT%d:%d:%d",
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
+ &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
+ tm.tm_mon -= 1;
+ tm.tm_year -= 1900;
*when = utc_mktime(&tm);
+
}
static void uemis_duration(char *buffer, void *_duration)
start_match("dive", name, buf);
switch (import_source) {
- case SUUNTO:
- if (suunto_dive_match(divep, name, len, buf))
- return;
- break;
-
case UEMIS:
if (uemis_dive_match(divep, name, len, buf))
return;
return;
if (MATCH(".buddy", utf8_string, &dive->buddy))
return;
-
+ if (MATCH(".rating", get_index, &dive->rating))
+ return;
if (MATCH(".cylinder.size", cylindersize, &dive->cylinder[cylinder_index].type.size))
return;
if (MATCH(".cylinder.workpressure", pressure, &dive->cylinder[cylinder_index].type.workingpressure))
memset(&tm, 0, sizeof(tm));
}
-static void sanitize_gasmix(struct gasmix *mix)
-{
- unsigned int o2, he;
-
- o2 = mix->o2.permille;
- he = mix->he.permille;
-
- /* Regular air: leave empty */
- if (!he) {
- if (!o2)
- return;
- /* 20.9% or 21% O2 is just air */
- if (o2 >= 209 && o2 <= 210) {
- mix->o2.permille = 0;
- return;
- }
- }
-
- /* Sane mix? */
- if (o2 <= 1000 && he <= 1000 && o2+he <= 1000)
- return;
- fprintf(stderr, "Odd gasmix: %d O2 %d He\n", o2, he);
- memset(mix, 0, sizeof(*mix));
-}
-
-/*
- * See if the size/workingpressure looks like some standard cylinder
- * size, eg "AL80".
- */
-static void match_standard_cylinder(cylinder_type_t *type)
-{
- double cuft;
- int psi, len;
- const char *fmt;
- char buffer[20], *p;
-
- /* Do we already have a cylinder description? */
- if (type->description)
- return;
-
- cuft = type->size.mliter / 28317.0;
- cuft *= to_ATM(type->workingpressure);
- psi = type->workingpressure.mbar / 68.95;
-
- switch (psi) {
- case 2300 ... 2500: /* 2400 psi: LP tank */
- fmt = "LP%d";
- break;
- case 2600 ... 2700: /* 2640 psi: LP+10% */
- fmt = "LP%d";
- break;
- case 2900 ... 3100: /* 3000 psi: ALx tank */
- fmt = "AL%d";
- break;
- case 3400 ... 3500: /* 3442 psi: HP tank */
- fmt = "HP%d";
- break;
- case 3700 ... 3850: /* HP+10% */
- fmt = "HP%d+";
- break;
- default:
- return;
- }
- len = snprintf(buffer, sizeof(buffer), fmt, (int) (cuft+0.5));
- p = malloc(len+1);
- if (!p)
- return;
- memcpy(p, buffer, len+1);
- type->description = p;
-}
-
-
-/*
- * There are two ways to give cylinder size information:
- * - total amount of gas in cuft (depends on working pressure and physical size)
- * - physical size
- *
- * where "physical size" is the one that actually matters and is sane.
- *
- * We internally use physical size only. But we save the workingpressure
- * so that we can do the conversion if required.
- */
-static void sanitize_cylinder_type(cylinder_type_t *type)
-{
- double volume_of_air, atm, volume;
-
- /* If we have no working pressure, it had *better* be just a physical size! */
- if (!type->workingpressure.mbar)
- return;
-
- /* No size either? Nothing to go on */
- if (!type->size.mliter)
- return;
-
- if (input_units.volume == CUFT || import_source == SUUNTO) {
- volume_of_air = type->size.mliter * 28.317; /* milli-cu ft to milliliter */
- atm = to_ATM(type->workingpressure); /* working pressure in atm */
- volume = volume_of_air / atm; /* milliliters at 1 atm: "true size" */
- type->size.mliter = volume + 0.5;
- }
-
- /* Ok, we have both size and pressure: try to match a description */
- match_standard_cylinder(type);
-}
-
-static void sanitize_cylinder_info(struct dive *dive)
-{
- int i;
-
- for (i = 0; i < MAX_CYLINDERS; i++) {
- sanitize_gasmix(&dive->cylinder[i].gasmix);
- sanitize_cylinder_type(&dive->cylinder[i].type);
- }
-}
-
static void dive_end(void)
{
if (!dive)
return;
- sanitize_cylinder_info(dive);
record_dive(dive);
dive = NULL;
cylinder_index = 0;
if (!dive)
return;
- finish_sample(dive, sample);
+ finish_sample(dive);
sample = NULL;
}
traverse(n->children);
}
-static void suunto_importer(void)
-{
- import_source = SUUNTO;
- input_units = SI_units;
-}
-
static void uemis_importer(void)
{
import_source = UEMIS;
{ "P", sample_start, sample_end },
/* Import type recognition */
- { "SUUNTO", suunto_importer },
{ "Divinglog", DivingLog_importer },
{ "pre_dive", uemis_importer },
{ "dives", uemis_importer },
import_source = UNKNOWN;
}
+struct memblock {
+ void *buffer;
+ size_t size;
+};
+
+static int readfile(const char *filename, struct memblock *mem)
+{
+ int ret, fd = open(filename, O_RDONLY);
+ struct stat st;
+
+ mem->buffer = NULL;
+ mem->size = 0;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ return fd;
+ ret = fstat(fd, &st);
+ if (ret < 0)
+ goto out;
+ ret = -EINVAL;
+ if (!S_ISREG(st.st_mode))
+ goto out;
+ ret = 0;
+ if (!st.st_size)
+ goto out;
+ mem->buffer = malloc(st.st_size);
+ ret = -1;
+ errno = ENOMEM;
+ if (!mem->buffer)
+ goto out;
+ mem->size = st.st_size;
+ ret = read(fd, mem->buffer, mem->size);
+ if (ret < 0)
+ goto free;
+ if (ret == mem->size)
+ goto out;
+ errno = EIO;
+ ret = -1;
+free:
+ free(mem->buffer);
+ mem->buffer = NULL;
+ mem->size = 0;
+out:
+ close(fd);
+ return ret;
+}
+
void parse_xml_file(const char *filename, GError **error)
{
xmlDoc *doc;
+ struct memblock mem;
+
+ if (readfile(filename, &mem) < 0) {
+ fprintf(stderr, "Failed to read '%s'.\n", filename);
+ if (error) {
+ *error = g_error_new(g_quark_from_string("subsurface"),
+ DIVE_ERROR_PARSE,
+ "Failed to read '%s'",
+ filename);
+ }
+ return;
+ }
- doc = xmlReadFile(filename, NULL, 0);
+ doc = xmlReadMemory(mem.buffer, mem.size, filename, NULL, 0);
if (!doc) {
fprintf(stderr, "Failed to parse '%s'.\n", filename);
if (error != NULL)
set_filename(filename);
reset_all();
dive_start();
+#ifdef XSLT
+ doc = test_xslt_transforms(doc);
+#endif
traverse(xmlDocGetRootElement(doc));
dive_end();
xmlFreeDoc(doc);
{
LIBXML_TEST_VERSION
}
+
+#ifdef XSLT
+
+/* Maybe we'll want a environment variable that can override this.. */
+static const char *xslt_path = XSLT ":xslt:.";
+
+static xsltStylesheetPtr try_get_stylesheet(const char *path, int len, const char *name)
+{
+ xsltStylesheetPtr ret;
+ int namelen = strlen(name);
+ char *filename = malloc(len+1+namelen+1);
+
+ if (!filename)
+ return NULL;
+
+ memcpy(filename, path, len);
+ filename[len] = G_DIR_SEPARATOR;
+ memcpy(filename + len + 1, name, namelen+1);
+
+ ret = NULL;
+ if (!access(filename, R_OK))
+ ret = xsltParseStylesheetFile(filename);
+ free(filename);
+
+ return ret;
+}
+
+static xsltStylesheetPtr get_stylesheet(const char *name)
+{
+ const char *path = xslt_path, *next;
+
+ do {
+ int len;
+ xsltStylesheetPtr ret;
+
+ next = strchr(path, ':');
+ len = strlen(path);
+ if (next) {
+ len = next - path;
+ next++;
+ }
+ ret = try_get_stylesheet(path, len, name);
+ if (ret)
+ return ret;
+ } while ((path = next) != NULL);
+
+ return NULL;
+}
+
+static struct xslt_files {
+ const char *root;
+ const char *file;
+} xslt_files[] = {
+ { "SUUNTO", "SuuntoSDM.xslt" },
+ { "JDiveLog", "jdivelog2subsurface.xslt" },
+ { NULL, }
+};
+
+xmlDoc *test_xslt_transforms(xmlDoc *doc)
+{
+ struct xslt_files *info = xslt_files;
+ xmlDoc *transformed;
+ xsltStylesheetPtr xslt = NULL;
+ xmlNode *root_element = xmlDocGetRootElement(doc);
+
+ while ((info->root) && (strcasecmp(root_element->name, info->root) != 0)) {
+ info++;
+ }
+
+ if (info->root) {
+ xmlSubstituteEntitiesDefault(1);
+ xslt = get_stylesheet(info->file);
+ if (xslt == NULL)
+ return doc;
+ transformed = xsltApplyStylesheet(xslt, doc, NULL);
+ xmlFreeDoc(doc);
+ xsltFreeStylesheet(xslt);
+ return transformed;
+ }
+ return doc;
+}
+#endif