diff options
-rw-r--r-- | file.c | 39 | ||||
-rw-r--r-- | parse-xml.c | 23 | ||||
-rw-r--r-- | xslt/csv2xml.xslt | 110 |
3 files changed, 169 insertions, 3 deletions
@@ -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> |