Changeset d7cf340 in subsurface


Ignore:
Timestamp:
May 7, 2017, 7:52:55 AM (5 months ago)
Author:
Dirk Hohndel <dirk@…>
Branches:
master
Children:
ccf9163
Parents:
75762e5f (diff), 21cdc64 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge branch 'seabear-refactor'

Signed-off-by: Dirk Hohndel <dirk@…>

Files:
9 edited

Legend:

Unmodified
Added
Removed
  • core/dive.h

    rb368ecd r0dfa448  
    705705extern int parse_file(const char *filename);
    706706extern int parse_csv_file(const char *filename, char **params, int pnr, const char *csvtemplate);
     707extern int parse_seabear_log(const char *filename);
    707708extern int parse_seabear_csv_file(const char *filename, char **params, int pnr, const char *csvtemplate);
    708709extern int parse_txt_file(const char *filename, const char *csv);
  • core/file.c

    r25cec35d rd7cf340  
    10571057
    10581058#define SBPARAMS 40
     1059int parse_seabear_log(const char *filename)
     1060{
     1061        char *params[SBPARAMS];
     1062        int pnr = 0;
     1063
     1064        pnr = parse_seabear_header(filename, params, pnr);
     1065
     1066        if (parse_seabear_csv_file(filename, params, pnr, "csv") < 0) {
     1067                return -1;
     1068        }
     1069
     1070        return 0;
     1071}
     1072
    10591073int parse_seabear_csv_file(const char *filename, char **params, int pnr, const char *csvtemplate)
    10601074{
  • core/qthelper.cpp

    r92d24a2 rd7cf340  
    15201520        return uuidString;
    15211521}
     1522
     1523int parse_seabear_header(const char *filename, char **params, int pnr)
     1524{
     1525        QFile f(filename);
     1526
     1527        f.open(QFile::ReadOnly);
     1528        QString parseLine = f.readLine();
     1529
     1530        /*
     1531         * Parse dive number from Seabear CSV header
     1532         */
     1533
     1534        while ((parseLine = f.readLine().trimmed()).length() > 0 && !f.atEnd()) {
     1535                if (parseLine.contains("//DIVE NR: ")) {
     1536                        params[pnr++] = strdup("diveNro");
     1537                        params[pnr++] = strdup(parseLine.replace(QString::fromLatin1("//DIVE NR: "), QString::fromLatin1("")).toUtf8().data());
     1538                        break;
     1539                }
     1540        }
     1541        f.seek(0);
     1542
     1543        /*
     1544         * Parse header - currently only interested in sample
     1545         * interval and hardware version. If we have old format
     1546         * the interval value is missing from the header.
     1547         */
     1548
     1549        while ((parseLine = f.readLine().trimmed()).length() > 0 && !f.atEnd()) {
     1550                if (parseLine.contains("//Hardware Version: ")) {
     1551                        params[pnr++] = strdup("hw");
     1552                        params[pnr++] = strdup(parseLine.replace(QString::fromLatin1("//Hardware Version: "), QString::fromLatin1("\"Seabear ")).trimmed().append("\"").toUtf8().data());
     1553                        break;
     1554                }
     1555        }
     1556        f.seek(0);
     1557
     1558        /*
     1559         * Grab the sample interval
     1560         */
     1561
     1562        while ((parseLine = f.readLine().trimmed()).length() > 0 && !f.atEnd()) {
     1563                if (parseLine.contains("//Log interval: ")) {
     1564                        params[pnr++] = strdup("delta");
     1565                        params[pnr++] = strdup(parseLine.remove(QString::fromLatin1("//Log interval: ")).trimmed().remove(QString::fromLatin1(" s")).toUtf8().data());
     1566                        break;
     1567                }
     1568        }
     1569        f.seek(0);
     1570
     1571        /*
     1572         * Dive mode, can be: OC, APNEA, BOTTOM TIMER, CCR, CCR SENSORBOARD
     1573         * Note that we scan over the "Log interval" on purpose
     1574         */
     1575
     1576        while ((parseLine = f.readLine().trimmed()).length() > 0 && !f.atEnd()) {
     1577                QString needle = "//Mode: ";
     1578                if (parseLine.contains(needle)) {
     1579                        params[pnr++] = strdup("diveMode");
     1580                        params[pnr++] = strdup(parseLine.replace(needle, QString::fromLatin1("")).prepend("\"").append("\"").toUtf8().data());
     1581                }
     1582        }
     1583        f.seek(0);
     1584
     1585        while ((parseLine = f.readLine().trimmed()).length() > 0 && !f.atEnd()) {
     1586        }
     1587
     1588        /*
     1589         * Parse CSV fields
     1590         */
     1591
     1592        parseLine = f.readLine().trimmed();
     1593
     1594        QStringList currColumns = parseLine.split(';');
     1595        unsigned short index = 0;
     1596        Q_FOREACH (QString columnText, currColumns) {
     1597                if (columnText == "Time") {
     1598                        params[pnr++] = strdup("timeField");
     1599                        params[pnr++] = intdup(index++);
     1600                } else if (columnText == "Depth") {
     1601                        params[pnr++] = strdup("depthField");
     1602                        params[pnr++] = intdup(index++);
     1603                } else if (columnText == "Temperature") {
     1604                        params[pnr++] = strdup("tempField");
     1605                        params[pnr++] = intdup(index++);
     1606                } else if (columnText == "NDT") {
     1607                        params[pnr++] = strdup("ndlField");
     1608                        params[pnr++] = intdup(index++);
     1609                } else if (columnText == "TTS") {
     1610                        params[pnr++] = strdup("ttsField");
     1611                        params[pnr++] = intdup(index++);
     1612                } else if (columnText == "pO2_1") {
     1613                        params[pnr++] = strdup("o2sensor1Field");
     1614                        params[pnr++] = intdup(index++);
     1615                } else if (columnText == "pO2_2") {
     1616                        params[pnr++] = strdup("o2sensor2Field");
     1617                        params[pnr++] = intdup(index++);
     1618                } else if (columnText == "pO2_3") {
     1619                        params[pnr++] = strdup("o2sensor3Field");
     1620                        params[pnr++] = intdup(index++);
     1621                } else if (columnText == "Ceiling") {
     1622                        /* TODO: Add support for dive computer reported ceiling*/
     1623                        params[pnr++] = strdup("ceilingField");
     1624                        params[pnr++] = intdup(index++);
     1625                } else if (columnText == "Tank pressure") {
     1626                        params[pnr++] = strdup("pressureField");
     1627                        params[pnr++] = intdup(index++);
     1628                } else {
     1629                        // We do not know about this value
     1630                        qDebug() << "Seabear import found an un-handled field: " << columnText;
     1631                }
     1632        }
     1633
     1634        /* Separator is ';' and the index for that in DiveLogImportDialog constructor is 2 */
     1635        params[pnr++] = strdup("separatorIndex");
     1636        params[pnr++] = intdup(2);
     1637
     1638        /* And metric units */
     1639        params[pnr++] = strdup("units");
     1640        params[pnr++] = intdup(0);
     1641
     1642        params[pnr] = NULL;
     1643        f.close();
     1644        return pnr;
     1645}
     1646
     1647char *intdup(int index)
     1648{
     1649        char tmpbuf[21];
     1650
     1651        snprintf(tmpbuf, sizeof(tmpbuf) - 2, "%d", index);
     1652        tmpbuf[20] = 0;
     1653        return strdup(tmpbuf);
     1654}
  • core/qthelper.h

    r4627973 rd7cf340  
    4848QString getUUID();
    4949QStringList imageExtensionFilters();
     50char *intdup(int index);
     51extern "C" int parse_seabear_header(const char *filename, char **params, int pnr);
    5052
    5153#endif // QTHELPER_H
  • core/qthelperfromc.h

    rb368ecd r0dfa448  
    2121const char *subsurface_user_agent();
    2222enum deco_mode decoMode();
     23int parse_seabear_header(const char *filename, char **params, int pnr);
    2324
    2425#endif // QTHELPERFROMC_H
  • desktop-widgets/divelogimportdialog.cpp

    r9021a44 r0dfa448  
    88#include <QMimeData>
    99#include <QRegExp>
     10#include "core/qthelper.h"
    1011
    1112static QString subsurface_mimedata = "subsurface/csvcolumns";
     
    741742                if (!headers.at(i).isEmpty())
    742743                        resultModel->setData(resultModel->index(0, i),headers.at(i),Qt::EditRole);
    743 }
    744 
    745 char *intdup(int index)
    746 {
    747         char tmpbuf[21];
    748 
    749         snprintf(tmpbuf, sizeof(tmpbuf) - 2, "%d", index);
    750         tmpbuf[20] = 0;
    751         return strdup(tmpbuf);
    752744}
    753745
     
    855847                for (int i = 0; i < fileNames.size(); ++i) {
    856848                        if (ui->knownImports->currentText() == "Seabear CSV") {
    857                                 char *params[40];
    858                                 int pnr = 0;
    859 
    860                                 params[pnr++] = strdup("timeField");
    861                                 params[pnr++] = intdup(r.indexOf(tr("Sample time")));
    862                                 params[pnr++] = strdup("depthField");
    863                                 params[pnr++] = intdup(r.indexOf(tr("Sample depth")));
    864                                 params[pnr++] = strdup("tempField");
    865                                 params[pnr++] = intdup(r.indexOf(tr("Sample temperature")));
    866                                 params[pnr++] = strdup("po2Field");
    867                                 params[pnr++] = intdup(r.indexOf(tr("Sample pO₂")));
    868                                 params[pnr++] = strdup("o2sensor1Field");
    869                                 params[pnr++] = intdup(r.indexOf(tr("Sample sensor1 pO₂")));
    870                                 params[pnr++] = strdup("o2sensor2Field");
    871                                 params[pnr++] = intdup(r.indexOf(tr("Sample sensor2 pO₂")));
    872                                 params[pnr++] = strdup("o2sensor3Field");
    873                                 params[pnr++] = intdup(r.indexOf(tr("Sample sensor3 pO₂")));
    874                                 params[pnr++] = strdup("cnsField");
    875                                 params[pnr++] = intdup(r.indexOf(tr("Sample CNS")));
    876                                 params[pnr++] = strdup("ndlField");
    877                                 params[pnr++] = intdup(r.indexOf(tr("Sample NDL")));
    878                                 params[pnr++] = strdup("ttsField");
    879                                 params[pnr++] = intdup(r.indexOf(tr("Sample TTS")));
    880                                 params[pnr++] = strdup("stopdepthField");
    881                                 params[pnr++] = intdup(r.indexOf(tr("Sample stopdepth")));
    882                                 params[pnr++] = strdup("pressureField");
    883                                 params[pnr++] = intdup(r.indexOf(tr("Sample pressure")));
    884                                 params[pnr++] = strdup("setpointFiend");
    885                                 params[pnr++] = intdup(-1);
    886                                 params[pnr++] = strdup("separatorIndex");
    887                                 params[pnr++] = intdup(ui->CSVSeparator->currentIndex());
    888                                 params[pnr++] = strdup("units");
    889                                 params[pnr++] = intdup(ui->CSVUnits->currentIndex());
    890                                 params[pnr++] = strdup("delta");
    891                                 params[pnr++] = strdup(delta.toUtf8().data());
    892                                 if (hw.length()) {
    893                                         params[pnr++] = strdup("hw");
    894                                         params[pnr++] = strdup(hw.toUtf8().data());
    895                                 }
    896                                 params[pnr++] = NULL;
    897 
    898                                 if (parse_seabear_csv_file(fileNames[i].toUtf8().data(),
    899                                                         params, pnr - 1, "csv") < 0) {
    900                                         return;
    901                                 }
    902                                 // Seabear CSV stores NDL and TTS in Minutes, not seconds
    903                                 struct dive *dive = dive_table.dives[dive_table.nr - 1];
    904                                 for(int s_nr = 0 ; s_nr < dive->dc.samples ; s_nr++) {
    905                                         struct sample *sample = dive->dc.sample + s_nr;
    906                                         sample->ndl.seconds *= 60;
    907                                         sample->tts.seconds *= 60;
    908                                 }
     849
     850                                parse_seabear_log(fileNames[i].toUtf8().data());
     851
    909852                        } else {
    910853                                char *params[49];
  • dives/TestDiveSeabearNewFormat.xml

    r261c11d r21cdc64  
    66</divesites>
    77<dives>
    8 <dive date='2009-10-10' time='05:32:41' duration='16:25 min'>
    9   <divecomputer model='Imported from CSV' deviceid='ffffffff'>
     8<dive number='2' date='2012-10-01' time='06:22:00' duration='16:25 min'>
     9  <divecomputer model='Seabear H3' deviceid='ffffffff' dctype='CCR'>
    1010  <depth max='69.9 m' mean='32.928 m' />
    1111  <temperature water='25.0 C' />
     
    210210  </divecomputer>
    211211</dive>
    212 <dive date='2009-10-10' time='07:32:41' duration='16:25 min'>
    213   <divecomputer model='Imported from CSV' deviceid='ffffffff'>
     212<dive number='3' date='2012-10-01' time='06:49:00' duration='16:25 min'>
     213  <divecomputer model='Seabear H3' deviceid='ffffffff'>
    214214  <depth max='69.9 m' mean='32.928 m' />
    215215  <temperature water='25.0 C' />
     
    414414  </divecomputer>
    415415</dive>
    416 <dive date='2009-10-10' time='09:32:41' duration='16:17 min'>
    417   <divecomputer model='Imported from CSV' deviceid='ffffffff'>
     416<dive number='4' date='2012-10-01' time='07:07:00' duration='16:17 min'>
     417  <divecomputer model='Seabear H3' deviceid='ffffffff' dctype='Freedive'>
    418418  <depth max='70.1 m' mean='33.197 m' />
    419419  <temperature water='25.0 C' />
     
    13451345  </divecomputer>
    13461346</dive>
    1347 <dive date='2009-10-10' time='11:32:41' duration='16:25 min'>
    1348   <divecomputer model='Imported from CSV' deviceid='ffffffff'>
     1347<dive number='5' date='2012-10-01' time='07:24:00' duration='16:25 min'>
     1348  <divecomputer model='Seabear H3' deviceid='ffffffff'>
    13491349  <depth max='69.9 m' mean='32.928 m' />
    13501350  <temperature water='25.0 C' />
     
    15491549  </divecomputer>
    15501550</dive>
    1551 <dive date='2009-10-10' time='13:32:41' duration='16:25 min'>
     1551<dive number='1' date='2012-10-01' time='06:02:00' duration='16:25 min'>
    15521552  <cylinder description='oxygen' o2='100.0%' use='oxygen' />
    15531553  <cylinder description='diluent' use='diluent' />
    1554   <divecomputer model='Imported from CSV' deviceid='ffffffff' dctype='CCR' no_o2sensors='3'>
     1554  <divecomputer model='Seabear T1' deviceid='ffffffff' dctype='CCR' no_o2sensors='3'>
    15551555  <depth max='69.9 m' mean='32.928 m' />
    15561556  <temperature water='25.0 C' />
     
    17551755  </divecomputer>
    17561756</dive>
    1757 <dive date='2009-10-10' time='15:32:41' duration='16:25 min'>
    1758   <divecomputer model='Imported from CSV' deviceid='ffffffff'>
     1757<dive number='2' date='2012-10-01' time='06:22:00' duration='16:25 min'>
     1758  <divecomputer model='Seabear T1' deviceid='ffffffff' dctype='CCR'>
    17591759  <depth max='69.9 m' mean='32.928 m' />
    17601760  <temperature water='25.0 C' />
     
    19591959  </divecomputer>
    19601960</dive>
    1961 <dive date='2009-10-10' time='17:32:41' duration='16:25 min'>
    1962   <divecomputer model='Imported from CSV' deviceid='ffffffff'>
     1961<dive number='3' date='2012-10-01' time='06:49:00' duration='16:25 min'>
     1962  <divecomputer model='Seabear T1' deviceid='ffffffff'>
    19631963  <depth max='69.9 m' mean='32.928 m' />
    19641964  <temperature water='25.0 C' />
     
    21632163  </divecomputer>
    21642164</dive>
    2165 <dive date='2009-10-10' time='19:32:41' duration='16:17 min'>
    2166   <divecomputer model='Imported from CSV' deviceid='ffffffff'>
     2165<dive number='4' date='2012-10-01' time='07:07:00' duration='16:17 min'>
     2166  <divecomputer model='Seabear T1' deviceid='ffffffff' dctype='Freedive'>
    21672167  <depth max='70.1 m' mean='33.197 m' />
    21682168  <temperature water='24.0 C' />
     
    30943094  </divecomputer>
    30953095</dive>
    3096 <dive date='2009-10-10' time='21:32:41' duration='16:25 min'>
    3097   <divecomputer model='Imported from CSV' deviceid='ffffffff'>
     3096<dive number='5' date='2012-10-01' time='07:24:00' duration='16:25 min'>
     3097  <divecomputer model='Seabear T1' deviceid='ffffffff'>
    30983098  <depth max='69.9 m' mean='32.928 m' />
    30993099  <temperature water='25.0 C' />
  • tests/testparse.cpp

    rb7c6998 rd7cf340  
    55#include "core/divelist.h"
    66#include <QTextStream>
     7#include "core/qthelper.h"
    78
    89/* We have to use a macro since QCOMPARE
     
    4344}
    4445
    45 char *intdup(int index)
    46 {
    47         char tmpbuf[21];
    48 
    49         snprintf(tmpbuf, sizeof(tmpbuf) - 2, "%d", index);
    50         tmpbuf[20] = 0;
    51         return strdup(tmpbuf);
    52 }
    53 
    5446int TestParse::parseCSV(int units, std::string file)
    5547{
     
    253245
    254246        for (int i = 0; i < files.size(); ++i) {
    255                 QString delta;
    256                 QStringList currColumns;
    257                 QStringList headers;
    258                 QString file = QString::fromLatin1(SUBSURFACE_TEST_DATA "/dives/").append(files.at(i));
    259                 QFile f(file);
    260 
    261                 /*
    262                  * Parse the sample interval if available from CSV
    263                  * header.
    264                  */
    265 
    266                 f.open(QFile::ReadOnly);
    267                 while ((firstLine = f.readLine().trimmed()).length() > 0 && !f.atEnd()) {
    268                         if (firstLine.contains("//Log interval: "))
    269                                 delta = firstLine.remove(QString::fromLatin1("//Log interval: ")).trimmed().remove(QString::fromLatin1(" s"));
    270                 }
    271                 firstLine = f.readLine().trimmed();
    272 
    273                 /*
    274                  * Parse the field configuration from the CSV header.
    275                  */
    276 
    277                 currColumns = firstLine.split(';');
    278                 Q_FOREACH (QString columnText, currColumns) {
    279                         if (columnText == "Time") {
    280                                 headers.append("Sample time");
    281                         } else if (columnText == "Depth") {
    282                                 headers.append("Sample depth");
    283                         } else if (columnText == "Temperature") {
    284                                 headers.append("Sample temperature");
    285                         } else if (columnText == "NDT") {
    286                                 headers.append("Sample NDL");
    287                         } else if (columnText == "TTS") {
    288                                 headers.append("Sample TTS");
    289                         } else if (columnText == "pO2_1") {
    290                                 headers.append("Sample sensor1 pO₂");
    291                         } else if (columnText == "pO2_2") {
    292                                 headers.append("Sample sensor2 pO₂");
    293                         } else if (columnText == "pO2_3") {
    294                                 headers.append("Sample sensor3 pO₂");
    295                         } else if (columnText == "Ceiling") {
    296                                 headers.append("Sample ceiling");
    297                         } else if (columnText == "Tank pressure") {
    298                                 headers.append("Sample pressure");
    299                         } else {
    300                                 // We do not know about this value
    301                                 qDebug() << "Seabear import found an un-handled field: " << columnText;
    302                                 headers.append("");
    303                         }
    304                         f.close();
    305                 }
    306 
    307                 /*
    308                  * Validate the parsing routine returns success.
    309                  */
    310 
    311                 char *params[42];
    312                 int pnr = 0;
    313 
    314                 params[pnr++] = strdup("timeField");
    315                 params[pnr++] = intdup(headers.indexOf(tr("Sample time")));
    316                 params[pnr++] = strdup("depthField");
    317                 params[pnr++] = intdup(headers.indexOf(tr("Sample depth")));
    318                 params[pnr++] = strdup("tempField");
    319                 params[pnr++] = intdup(headers.indexOf(tr("Sample temperature")));
    320                 params[pnr++] = strdup("po2Field");
    321                 params[pnr++] = intdup(headers.indexOf(tr("Sample pO₂")));
    322                 params[pnr++] = strdup("o2sensor1Field");
    323                 params[pnr++] = intdup(headers.indexOf(tr("Sample sensor1 pO₂")));
    324                 params[pnr++] = strdup("o2sensor2Field");
    325                 params[pnr++] = intdup(headers.indexOf(tr("Sample sensor2 pO₂")));
    326                 params[pnr++] = strdup("o2sensor3Field");
    327                 params[pnr++] = intdup(headers.indexOf(tr("Sample sensor3 pO₂")));
    328                 params[pnr++] = strdup("cnsField");
    329                 params[pnr++] = intdup(headers.indexOf(tr("Sample CNS")));
    330                 params[pnr++] = strdup("ndlField");
    331                 params[pnr++] = intdup(headers.indexOf(tr("Sample NDL")));
    332                 params[pnr++] = strdup("ttsField");
    333                 params[pnr++] = intdup(headers.indexOf(tr("Sample TTS")));
    334                 params[pnr++] = strdup("stopdepthField");
    335                 params[pnr++] = intdup(headers.indexOf(tr("Sample stopdepth")));
    336                 params[pnr++] = strdup("pressureField");
    337                 params[pnr++] = intdup(headers.indexOf(tr("Sample pressure")));
    338                 params[pnr++] = strdup("setpointField");
    339                 params[pnr++] = intdup(-1);
    340                 params[pnr++] = strdup("numberField");
    341                 params[pnr++] = intdup(-1);
    342                 params[pnr++] = strdup("separatorIndex");
    343                 params[pnr++] = intdup(2);
    344                 params[pnr++] = strdup("units");
    345                 params[pnr++] = intdup(0);
    346                 params[pnr++] = strdup("delta");
    347                 params[pnr++] = strdup(delta.toUtf8().data());
    348                 params[pnr++] = NULL;
    349 
    350                 QCOMPARE(parse_seabear_csv_file(file.toUtf8().data(),
    351                                         params, pnr - 1, "csv"), 0);
    352 
     247
     248                QCOMPARE(parse_seabear_log(QString::fromLatin1(SUBSURFACE_TEST_DATA
     249                        "/dives/").append(files.at(i)).toLatin1().data()), 0);
    353250                QCOMPARE(dive_table.nr, i + 1);
    354 
    355                 /*
    356                  * Set artificial but static dive times so the result
    357                  * can be compared to saved one.
    358                  */
    359 
    360                 if (dive_table.nr > 0) {
    361                         dive = dive_table.dives[dive_table.nr - 1];
    362                         dive->when = 1255152761 + 7200 * i;
    363                         dive->dc.when = 1255152761 + 7200 * i;
    364                 }
    365251        }
    366252
  • xslt/csv2xml.xslt

    rb919a9d r6e1c000  
    2727  <xsl:param name="delta" select="delta"/>
    2828  <xsl:param name="hw" select="hw"/>
     29  <xsl:param name="diveNro" select="diveNro"/>
     30  <xsl:param name="diveMode" select="diveMode"/>
    2931  <xsl:output method="xml" indent="yes"/>
    3032
     
    112114          </xsl:if>
    113115
     116          <xsl:if test="string-length($diveNro) > 0">
     117            <xsl:attribute name="number">
     118              <xsl:value-of select="$diveNro"/>
     119            </xsl:attribute>
     120          </xsl:if>
     121
    114122          <!-- If the dive is CCR, create oxygen and diluent cylinders -->
    115123
     
    138146              </xsl:attribute>
    139147            </xsl:if>
     148
     149            <!-- Seabear specific dive modes -->
     150            <xsl:if test="string-length($diveMode) > 0">
     151              <xsl:if test="$diveMode = 'OC' or $diveMode = 'APNEA' or $diveMode = 'CCR' or $diveMode = 'CCR SENSORBOARD'">
     152                <xsl:attribute name="dctype">
     153                  <xsl:choose>
     154                    <xsl:when test="$diveMode = 'APNEA'">
     155                      <xsl:value-of select="'Freedive'"/>
     156                    </xsl:when>
     157                    <xsl:when test="$diveMode = 'CCR' or $diveMode = 'CCR SENSORBOARD' ">
     158                      <xsl:value-of select="'CCR'"/>
     159                    </xsl:when>
     160                  </xsl:choose>
     161                </xsl:attribute>
     162              </xsl:if>
     163            </xsl:if>
     164
    140165            <xsl:call-template name="printLine">
    141166              <xsl:with-param name="line" select="substring-before(//csv, $lf)"/>
Note: See TracChangeset for help on using the changeset viewer.