Flexible CSV export from FileMaker via XSLT

<?xml version="1.0" encoding="utf-8"?>
 
<!--
Help FileMaker to export CSV in a flexible way.
 
Version 0.1, 08. Sept 2016
Author Jens Teich, http://jensteich.de.
 
Fields can be separated by `,', `;', or anything else.  Just configure
template 'separate-fields'.  By default there will be no separator
after last field (at end of line).  This can also be customized quite
easily.  xsl:if test="position() < last()" does this.  Commenting all
lines containing xsl-if does it.
 
String values can be wrapped in `"# or not at all or anything else.
Just configure template 'wrap-strings'.
 
FileMaker field names can appear as first line or not at all.  If they
appear there is a template to transform them.
 
-->
 
<xsl:stylesheet version="1.0"
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
		xmlns:fmp="http://www.filemaker.com/fmpxmlresult"
		exclude-result-prefixes="fmp">
  <!-- Change encoding from `utf-8' to required value of output
       document -->
  <xsl:output method="text"
	      version="1.0" encoding="utf-8"
	      indent="no"/>
 
  <!-- Show header line with field names? -->
  <xsl:variable name="show-headers" select="true()"/>
  <!-- xsl:variable name="show-headers" select="false()"/ -->
 
  <!-- if next line is active `;' is used as field separator -->
  <xsl:template name="separate-fields"><xsl:text>;</xsl:text></xsl:template>
  <!-- if next line is active `,' is used as field separator -->
  <!--xsl:template name="separate-fields"><xsl:text>,</xsl:text></xsl:template-->
 
  <!-- if next line is active, strings will be wrapped in `"' -->
  <xsl:template name="wrap-strings"><xsl:text>"</xsl:text></xsl:template>
  <!-- if next line is active, strings will not be wrapped at all -->
  <!-- xsl:template name="wrap-strings"><xsl:text></xsl:text></xsl:template -->
 
  <xsl:variable name="cut-left" select="1"/>
 
  <!-- Customize this template to process FileMaker filed names.  This
       example cuts CUT-LEFT letters and returns the rest.
  -->
  <xsl:template name="process-column-name">
    <xsl:param name="string-to-transform"/>
    <xsl:value-of select="substring($string-to-transform, $cut-left)"/>
  </xsl:template>
 
  <xsl:template match="fmp:FMPXMLRESULT">
    <xsl:if test="$show-headers">
      <xsl:for-each select="fmp:METADATA/fmp:FIELD">
	<!-- xsl:text><xsl:value-of select="{$field-separator}"/></xsl:text -->
	<xsl:call-template name="process-column-name">
	  <xsl:with-param name="string-to-transform" select="@NAME"/>
	</xsl:call-template>
	<xsl:if test="position() &lt; last()">
	  <xsl:call-template name="separate-fields"/>
	</xsl:if>
      </xsl:for-each>
      <xsl:text>
</xsl:text>
    </xsl:if>
    <xsl:apply-templates select="//fmp:ROW"/>
  </xsl:template>
 
  <xsl:template match="fmp:ROW">
    <xsl:for-each select="fmp:COL">
      <xsl:variable name="pos" select="position()"/>
      <xsl:if test="/fmp:FMPXMLRESULT/fmp:METADATA/fmp:FIELD[$pos]/@TYPE='TEXT'">
	<xsl:call-template name="wrap-strings"/>
      </xsl:if>
      <xsl:value-of select="fmp:DATA"/>
      <xsl:if test="/fmp:FMPXMLRESULT/fmp:METADATA/fmp:FIELD[$pos]/@TYPE='TEXT'">
	<xsl:call-template name="wrap-strings"/>
      </xsl:if>
      <xsl:if test="position() &lt; last()">
	<xsl:call-template name="separate-fields"/>
      </xsl:if>
    </xsl:for-each>
    <xsl:text>
</xsl:text>
  </xsl:template>
 
  <xsl:template match="text()"/>
 
</xsl:stylesheet>