summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--file.c39
-rw-r--r--parse-xml.c23
-rw-r--r--xslt/csv2xml.xslt110
3 files changed, 169 insertions, 3 deletions
diff --git a/file.c b/file.c
index 70eb79ad4..53b03330c 100644
--- a/file.c
+++ b/file.c
@@ -97,6 +97,36 @@ static int try_to_open_zip(const char *filename, struct memblock *mem, char **er
return success;
}
+static int try_to_xslt_open_csv(const char *filename, struct memblock *mem, char **error)
+{
+ char *buf;
+
+ if (readfile(filename, mem) < 0) {
+ if (error) {
+ int len = strlen(_("Failed to read '%s'")) + strlen(filename);
+ *error = malloc(len);
+ snprintf(*error, len, _("Failed to read '%s'"), filename);
+ }
+
+ return 1;
+ }
+
+ /* Surround the CSV file content with XML tags to enable XSLT
+ * parsing
+ */
+ buf = realloc(mem->buffer, mem->size + strlen("<csv></csv>"));
+ if (buf != NULL) {
+ memmove(buf + 5, mem->buffer, mem->size);
+ memcpy(buf, "<csv>", 5);
+ memcpy(mem->buffer + mem->size + 5, "</csv>", 7);
+ mem->buffer = buf;
+ mem->size += strlen("<csv></csv>");
+ } else
+ return 1;
+
+ return 0;
+}
+
static int try_to_open_db(const char *filename, struct memblock *mem, char **error)
{
return parse_dm4_buffer(filename, mem->buffer, mem->size, &dive_table, error);
@@ -229,6 +259,10 @@ static int open_by_filename(const char *filename, const char *fmt, struct memblo
if (!strcasecmp(fmt, "SDE") || !strcasecmp(fmt, "ZIP") || !strcasecmp(fmt, "DLD"))
return try_to_open_zip(filename, mem, error);
+ /* CSV files */
+ if (!strcasecmp(fmt, "CSV"))
+ return try_to_xslt_open_csv(filename, mem, error);
+
#if ONCE_COCHRAN_IS_SUPPORTED
/* Truly nasty intentionally obfuscated Cochran Anal software */
if (!strcasecmp(fmt, "CAN"))
@@ -266,8 +300,9 @@ void parse_file(const char *filename, char **error)
return;
if (error) {
- *error = malloc(1024);
- snprintf(*error, 1024, _("Failed to read '%s'"), filename);
+ int len = strlen(_("Failed to read '%s'")) + strlen(filename);
+ *error = malloc(len);
+ snprintf(*error, len, _("Failed to read '%s'"), filename);
}
return;
diff --git a/parse-xml.c b/parse-xml.c
index 370388f4f..195eafeb9 100644
--- a/parse-xml.c
+++ b/parse-xml.c
@@ -1915,6 +1915,7 @@ static struct xslt_files {
{ "UDDF", "uddf.xslt" },
{ "profile", "udcf.xslt" },
{ "Divinglog", "DivingLog.xslt" },
+ { "csv", "csv2xml.xslt" },
{ NULL, }
};
@@ -1925,6 +1926,7 @@ static xmlDoc *test_xslt_transforms(xmlDoc *doc, char **error)
xsltStylesheetPtr xslt = NULL;
xmlNode *root_element = xmlDocGetRootElement(doc);
char *attribute;
+ char *params[3];
while ((info->root) && (strcasecmp(root_element->name, info->root) != 0)) {
info++;
@@ -1946,9 +1948,28 @@ static xmlDoc *test_xslt_transforms(xmlDoc *doc, char **error)
parser_error(error, _("Can't open stylesheet (%s)/%s"), xslt_path, info->file);
return doc;
}
- transformed = xsltApplyStylesheet(xslt, doc, NULL);
+
+ /*
+ * params is only used for CSV import, but it does not
+ * hurt if we supply unused parameters for other
+ * transforms as well.
+ *
+ * We should have a GUI set the parameters but currently
+ * we just have PoC how parameters would be handled.
+ *
+ * (Field 9 is temperature for XP5 import, field 15
+ * is temperature for AP Logviewer.
+ */
+
+ params[0] = strdup("tempField");
+ params[1] = strdup("15");
+ params[2] = NULL;
+
+ transformed = xsltApplyStylesheet(xslt, doc, (const char **)params);
xmlFreeDoc(doc);
xsltFreeStylesheet(xslt);
+ free(params[0]);
+ free(params[1]);
return transformed;
}
return doc;
diff --git a/xslt/csv2xml.xslt b/xslt/csv2xml.xslt
new file mode 100644
index 000000000..faa9e9605
--- /dev/null
+++ b/xslt/csv2xml.xslt
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:import href="commonTemplates.xsl"/>
+ <xsl:strip-space elements="*"/>
+ <xsl:param name="tempField" select="tempField"/>
+ <xsl:output method="xml" indent="yes"/>
+
+ <xsl:variable name="lf"><xsl:text>
+</xsl:text></xsl:variable>
+ <xsl:variable name="fs"><xsl:text> </xsl:text></xsl:variable>
+
+ <xsl:template match="/">
+ <divelog program="subsurface-import" version="2">
+ <dives>
+ <dive>
+ <divecomputerid deviceid="ffffffff" model="stone" />
+ <xsl:call-template name="printLine">
+ <xsl:with-param name="line" select="substring-before(//csv, $lf)"/>
+ <xsl:with-param name="remaining" select="substring-after(//csv, $lf)"/>
+ </xsl:call-template>
+ </dive>
+ </dives>
+ </divelog>
+ </xsl:template>
+
+ <xsl:template name="printLine">
+ <xsl:param name="line"/>
+ <xsl:param name="remaining"/>
+ <xsl:call-template name="printFields">
+ <xsl:with-param name="line" select="$line"/>
+ </xsl:call-template>
+ <xsl:if test="$remaining != ''">
+ <xsl:call-template name="printLine">
+ <xsl:with-param name="line" select="substring-before($remaining, $lf)"/>
+ <xsl:with-param name="remaining" select="substring-after($remaining, $lf)"/>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template name="printFields">
+ <xsl:param name="line"/>
+
+ <xsl:variable name="value">
+ <xsl:call-template name="getFieldByIndex">
+ <xsl:with-param name="index" select="0"/>
+ <xsl:with-param name="line" select="$line"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- First field should be dive time. If the value is not numeric,
+ we'll skip it. (We do also allow time in h:m:s notation.) -->
+
+ <xsl:if test="number($value) = $value or number(substring-before($value, ':')) = substring-before($value, ':')">
+ <sample>
+ <xsl:attribute name="time">
+ <xsl:choose>
+ <xsl:when test="number($value) = $value">
+ <!-- We assume time in seconds -->
+
+ <xsl:call-template name="sec2time">
+ <xsl:with-param name="timeSec">
+ <xsl:value-of select="$value"/>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- We assume time format h:m:s -->
+
+ <xsl:value-of select="concat(
+ substring-before($value, ':') * 60 + substring-before(substring-after($value, ':'), ':'),
+ ':',
+ substring-after(substring-after($value, ':'), ':')
+ )" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+
+ <xsl:attribute name="depth">
+ <xsl:call-template name="getFieldByIndex">
+ <xsl:with-param name="index" select="1"/>
+ <xsl:with-param name="line" select="$line"/>
+ </xsl:call-template>
+ </xsl:attribute>
+
+ <xsl:attribute name="temp">
+ <xsl:call-template name="getFieldByIndex">
+ <xsl:with-param name="index" select="$tempField"/>
+ <xsl:with-param name="line" select="$line"/>
+ </xsl:call-template>
+ </xsl:attribute>
+ </sample>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template name="getFieldByIndex">
+ <xsl:param name="index"/>
+ <xsl:param name="line"/>
+ <xsl:choose>
+ <xsl:when test="$index > 0">
+ <xsl:call-template name="getFieldByIndex">
+ <xsl:with-param name="index" select="$index -1"/>
+ <xsl:with-param name="line" select="substring-after($line, $fs)"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="substring-before($line,$fs)"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+</xsl:stylesheet>